├── .dockerignore ├── design └── logo.sketch ├── .gitignore ├── frontend ├── img │ ├── demo-750.png │ ├── logo-120.png │ ├── logo-256.png │ ├── demo-1000.png │ ├── demo-1770.png │ ├── clippingsbot.png │ ├── clippingsbot-5x.png │ ├── logo-white-256.png │ ├── clippingsbot-white.png │ ├── clippingsbot-white-5x.png │ └── skyliner-logo.svg └── less │ ├── includes │ ├── reset.less │ └── fluidable.less │ └── clippingsbot.less ├── test ├── __init__.py ├── util.py ├── test_command.py └── test_app.py ├── bin ├── migrate ├── repl ├── worker ├── create-migration ├── dev ├── qa-tunnel └── prod-tunnel ├── migrations ├── V2017_02_20_07_52_24__channel-id.sql ├── V2017_02_20_16_46_12__channel-id-pk.sql ├── V2017_02_20_16_50_13__team-pattern-indexes.sql ├── V2017_02_20_17_20_06__notifications-channel.sql ├── V2017_02_21_19_56_03__comments-url-notification.sql ├── V2017_02_20_06_41_47__mention-fields.sql └── V2017_02_16_19_58_34__init.sql ├── supervisord.conf ├── templates ├── short-header.jinja ├── installed.jinja ├── help.jinja ├── index.jinja ├── layout.jinja └── privacy.jinja ├── query.sql ├── requirements.txt ├── bot ├── patterns.py ├── mentions.py ├── monitor.py ├── worker.py ├── db.py ├── oauth.py ├── team.py ├── app.py ├── notify.py ├── crawl.py └── command.py ├── package.json ├── gulpfile.js ├── LICENSE ├── Dockerfile ├── README.md └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | env.yaml 2 | node_modules 3 | *pyc 4 | image.tar 5 | .env -------------------------------------------------------------------------------- /design/logo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/design/logo.sketch -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *pyc 2 | image.tar 3 | env.yaml 4 | node_modules 5 | static 6 | .env 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /frontend/img/demo-750.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/demo-750.png -------------------------------------------------------------------------------- /frontend/img/logo-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/logo-120.png -------------------------------------------------------------------------------- /frontend/img/logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/logo-256.png -------------------------------------------------------------------------------- /frontend/img/demo-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/demo-1000.png -------------------------------------------------------------------------------- /frontend/img/demo-1770.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/demo-1770.png -------------------------------------------------------------------------------- /frontend/img/clippingsbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/clippingsbot.png -------------------------------------------------------------------------------- /frontend/img/clippingsbot-5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/clippingsbot-5x.png -------------------------------------------------------------------------------- /frontend/img/logo-white-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/logo-white-256.png -------------------------------------------------------------------------------- /frontend/img/clippingsbot-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/clippingsbot-white.png -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | os.environ['TESTING'] = "1" 3 | 4 | from .test_app import * 5 | from .test_command import * 6 | -------------------------------------------------------------------------------- /frontend/img/clippingsbot-white-5x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcfunley/clippingsbot/HEAD/frontend/img/clippingsbot-white-5x.png -------------------------------------------------------------------------------- /bin/migrate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | flyway -user=$USER -password= -locations="filesystem:migrations" -url="jdbc:postgresql:clippingsbot" -schemas=clippingsbot migrate -------------------------------------------------------------------------------- /bin/repl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | env $(cat $DIR/../.env | xargs) python 6 | -------------------------------------------------------------------------------- /migrations/V2017_02_20_07_52_24__channel-id.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | alter table clippingsbot.team_patterns add column channel_id text not null; 3 | -------------------------------------------------------------------------------- /bin/worker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | env $(cat $DIR/../.env | xargs) python -m bot.worker 6 | -------------------------------------------------------------------------------- /test/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | from unittest.mock import Mock, patch 3 | 4 | def patch_env(settings): 5 | return patch.object(os, 'getenv', Mock(side_effect=settings.get)) 6 | -------------------------------------------------------------------------------- /bin/create-migration: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | VERSION="V$(date -u +'%Y_%m_%d_%H_%M_%S')" 6 | echo "-- -*- sql-dialect: postgres; -*-" > $DIR/../migrations/"$VERSION"__$1.sql 7 | -------------------------------------------------------------------------------- /migrations/V2017_02_20_16_46_12__channel-id-pk.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | alter table clippingsbot.team_patterns drop constraint team_patterns_pkey; 3 | 4 | alter table clippingsbot.team_patterns add primary key (team_id, pattern_id, channel_id); 5 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | env $(cat $DIR/../.env | xargs) gunicorn \ 6 | --reload \ 7 | -b 0.0.0.0:5000 \ 8 | -w 4 bot.app:app \ 9 | --log-file - --access-logfile - 10 | -------------------------------------------------------------------------------- /migrations/V2017_02_20_16_50_13__team-pattern-indexes.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | create index idx_team_patterns_for_team 3 | on clippingsbot.team_patterns (team_id); 4 | 5 | create index idx_team_patterns_for_channel 6 | on clippingsbot.team_patterns (team_id, channel_id, created desc); 7 | -------------------------------------------------------------------------------- /bin/qa-tunnel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IP=`aws --profile construct ec2 describe-instances --filters "Name=tag:environment,Values=qa" "Name=tag:application,Values=slack-clippingsbot" "Name=instance-state-name,Values=running" | jq -r .Reservations[0].Instances[0].NetworkInterfaces[0].Association.PublicIp` 4 | 5 | ssh ec2-user@$IP 6 | -------------------------------------------------------------------------------- /bin/prod-tunnel: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | IP=`aws --profile construct ec2 describe-instances --filters "Name=tag:environment,Values=prod" "Name=tag:application,Values=slack-clippingsbot" "Name=instance-state-name,Values=running" | jq -r .Reservations[0].Instances[0].NetworkInterfaces[0].Association.PublicIp` 4 | 5 | ssh ec2-user@$IP 6 | -------------------------------------------------------------------------------- /migrations/V2017_02_20_17_20_06__notifications-channel.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | alter table clippingsbot.notifications 3 | add column channel_id text not null, 4 | drop constraint notifications_pkey; 5 | 6 | alter table clippingsbot.notifications 7 | add primary key (team_id, pattern_id, channel_id, feed, link_url); 8 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | 4 | [program:web] 5 | command=gunicorn -b 0.0.0.0:8080 -w 4 bot.app:app --log-file - --access-logfile - 6 | stdout_logfile=/dev/stdout 7 | stdout_logfile_maxbytes=0 8 | stderr_logfile=/dev/stderr 9 | stderr_logfile_maxbytes=0 10 | 11 | [program:worker] 12 | command=python -m bot.worker 13 | stdout_logfile=/dev/stdout 14 | stdout_logfile_maxbytes=0 15 | stderr_logfile=/dev/stderr 16 | stderr_logfile_maxbytes=0 -------------------------------------------------------------------------------- /templates/short-header.jinja: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

6 | Clippingsbot 7 | 8 | by Skyliner 9 | 10 |

11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /query.sql: -------------------------------------------------------------------------------- 1 | select m.pattern_id, m.feed, m.title, m.comments_url, m.link_url, 2 | tp.team_id, tp.channel_id 3 | from clippingsbot.mentions m 4 | left join clippingsbot.team_patterns tp on m.pattern_id = tp.pattern_id 5 | where not exists ( 6 | select 1 7 | from clippingsbot.notifications n 8 | where n.team_id = tp.team_id 9 | and n.pattern_id = tp.pattern_id 10 | and n.feed = m.feed 11 | and n.link_url = m.link_url 12 | and n.channel_id = tp.channel_id 13 | ) 14 | order by m.created asc; 15 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | amqp==2.1.4 2 | billiard==3.5.0.2 3 | boto==2.45.0 4 | click==6.6 5 | docopt==0.6.2 6 | feedparser==5.2.1 7 | Flask==0.11.1 8 | gunicorn==19.6.0 9 | itsdangerous==0.24 10 | Jinja2==2.8 11 | kombu==4.0.2 12 | MarkupSafe==0.23 13 | psycopg2==2.6.2 14 | pyaml==16.12.2 15 | pytz==2016.10 16 | PyYAML==3.12 17 | requests==2.13.0 18 | six==1.10.0 19 | slackclient==1.0.5 20 | SQLAlchemy==1.1.5 21 | stop-words==2015.2.23.1 22 | tablib==0.11.4 23 | vine==1.1.3 24 | websocket-client==0.40.0 25 | Werkzeug==0.11.10 26 | -------------------------------------------------------------------------------- /migrations/V2017_02_21_19_56_03__comments-url-notification.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | alter table clippingsbot.mentions 3 | drop constraint mentions_pkey; 4 | 5 | alter table clippingsbot.mentions 6 | add primary key (pattern_id, feed, comments_url); 7 | 8 | alter table clippingsbot.notifications 9 | add column comments_url text not null, 10 | drop constraint notifications_pkey; 11 | 12 | alter table clippingsbot.notifications 13 | add primary key (team_id, pattern_id, channel_id, feed, comments_url); 14 | -------------------------------------------------------------------------------- /bot/patterns.py: -------------------------------------------------------------------------------- 1 | from bot import db 2 | 3 | def save(pattern): 4 | sql = """ 5 | insert into clippingsbot.patterns (pattern) 6 | values (lower(:pattern)) 7 | on conflict(lower(pattern)) do update 8 | set pattern = lower(:pattern) 9 | returning pattern_id 10 | """ 11 | return db.scalar(sql, pattern=pattern) 12 | 13 | def find_all(): 14 | return db.find(""" 15 | select * 16 | from clippingsbot.patterns p 17 | where exists ( 18 | select 1 from clippingsbot.team_patterns tp 19 | where tp.pattern_id = p.pattern_id 20 | )""") 21 | -------------------------------------------------------------------------------- /bot/mentions.py: -------------------------------------------------------------------------------- 1 | from bot import db 2 | 3 | 4 | def save(pattern, mention): 5 | sql = """ 6 | insert into clippingsbot.mentions ( 7 | pattern_id, feed, title, comments_url, link_url 8 | ) values ( 9 | :pattern_id, :feed, :title, :comments_url, :link_url 10 | ) 11 | on conflict (pattern_id, feed, comments_url) do nothing 12 | returning 1 13 | """ 14 | return db.scalar( 15 | sql, pattern_id=pattern['pattern_id'], feed=mention['feed'], 16 | title=mention['title'], comments_url=mention['comments_url'], 17 | link_url=mention['link_url'] 18 | ) 19 | -------------------------------------------------------------------------------- /bot/monitor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | from concurrent.futures import ThreadPoolExecutor 4 | 5 | 6 | executor = ThreadPoolExecutor(max_workers=1) 7 | 8 | 9 | def _notify(message): 10 | webhook_url = os.getenv('FEEDBACK_WEBHOOK_URL', None) 11 | if not webhook_url: 12 | return 13 | 14 | env_text = '' if os.getenv('ENV', '') == 'prod' else ' (TESTING)' 15 | 16 | requests.post(webhook_url, json={ 17 | 'text': '%s%s' % (message, env_text), 18 | 'username': 'clippingsbot', 19 | 'icon_url': 'https://www.clippingsbot.com/static/img/logo-120.png', 20 | }) 21 | 22 | 23 | def notify(message): 24 | executor.submit(_notify, message) 25 | -------------------------------------------------------------------------------- /bot/worker.py: -------------------------------------------------------------------------------- 1 | from bot import crawl, notify 2 | from boto import sqs 3 | import json 4 | import os 5 | 6 | 7 | def run(): 8 | conn = sqs.connect_to_region(os.getenv('SQS_REGION')) 9 | q = conn.get_queue(os.getenv('SQS_QUEUE')) 10 | 11 | while 1: 12 | for message in conn.receive_message(q, wait_time_seconds=5): 13 | data = json.loads(message.get_body()) 14 | if data.get('message_type', None) == 'crawl': 15 | crawl.run() 16 | elif data.get('message_type', None) == 'notify': 17 | notify.run() 18 | 19 | conn.delete_message(q, message) 20 | print('Deleted message: %s' % message.get_body()) 21 | 22 | 23 | if __name__ == '__main__': 24 | run() 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clippingsbot", 3 | "version": "1.0.0", 4 | "description": "## Dev setup", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/skylinerio/clippingsbot.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/skylinerio/clippingsbot/issues" 17 | }, 18 | "homepage": "https://github.com/skylinerio/clippingsbot#readme", 19 | "devDependencies": { 20 | "gulp": "^3.9.1", 21 | "gulp-favicons": "^2.2.7", 22 | "gulp-less": "^3.3.0", 23 | "gulp-livereload": "^3.8.1", 24 | "gulp-sourcemaps": "^2.4.1" 25 | }, 26 | "dependencies": {} 27 | } 28 | -------------------------------------------------------------------------------- /migrations/V2017_02_20_06_41_47__mention-fields.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | drop table clippingsbot.mentions; 3 | 4 | create table clippingsbot.mentions ( 5 | pattern_id bigint not null references clippingsbot.patterns (pattern_id), 6 | feed text not null, 7 | title text not null, 8 | comments_url text not null, 9 | link_url text not null, 10 | created timestamp with time zone not null default current_timestamp, 11 | primary key (pattern_id, feed, link_url) 12 | ); 13 | 14 | 15 | drop table clippingsbot.notifications; 16 | 17 | create table clippingsbot.notifications ( 18 | team_id text not null references clippingsbot.teams (team_id), 19 | pattern_id bigint not null references clippingsbot.patterns (pattern_id), 20 | feed text not null, 21 | link_url text not null, 22 | 23 | created timestamp with time zone not null default current_timestamp, 24 | 25 | primary key (team_id, pattern_id, feed, link_url) 26 | ); 27 | -------------------------------------------------------------------------------- /bot/db.py: -------------------------------------------------------------------------------- 1 | import os 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.sql import text 4 | 5 | dburl = '%s?user=%s&password=%s' % ( 6 | os.getenv('DATABASE_URL'), 7 | os.getenv('DATABASE_USER'), 8 | os.getenv('DATABASE_PASSWORD')) 9 | 10 | if not os.getenv('TESTING', None): 11 | engine = create_engine(dburl) 12 | else: 13 | engine = None 14 | 15 | 16 | def connect(): 17 | return engine.connect() 18 | 19 | 20 | def execute(sql, **args): 21 | return engine.execute(text(sql), **args) 22 | 23 | 24 | def find_one(sql, **args): 25 | r = execute(sql, **args) 26 | return { k: v for k, v in zip(r.keys(), r.first()) } 27 | 28 | 29 | def find(sql, **args): 30 | r = execute(sql, **args) 31 | ks = r.keys() 32 | return ({ k: v for k, v in zip(ks, row)} for row in r.fetchall()) 33 | 34 | 35 | def scalar(sql, **args): 36 | r = execute(sql, **args).first() 37 | if r: 38 | return r[0] 39 | return None 40 | -------------------------------------------------------------------------------- /templates/installed.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout.jinja" %} 2 | 3 | {% block content %} 4 | 5 | {% include "short-header.jinja" %} 6 | 7 |
8 |
9 |
10 |
11 |

12 | Ok! You're all set. To get started, try some commands in Slack. 13 |

14 | 15 |

16 | Use this to watch for a term or phrase: 17 | 18 | /clippingsbot watch mycompany.com 19 | 20 |

21 | 22 |

23 | Use this to get a full list of commands: 24 | 25 | /clippingsbot help 26 | 27 |

28 |
29 |
30 |
31 |
32 | 33 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var less = require('gulp-less'); 3 | var path = require('path'); 4 | var sourcemaps = require('gulp-sourcemaps'); 5 | var livereload = require('gulp-livereload'); 6 | var favicons = require('gulp-favicons'); 7 | var util = require('gulp-util'); 8 | 9 | gulp.task('images', function() { 10 | return gulp.src('./frontend/img/*') 11 | .pipe(gulp.dest('./static/img')); 12 | }); 13 | 14 | gulp.task('favicons', function() { 15 | return gulp.src('./frontend/img/clippingsbot.png') 16 | .pipe(favicons({})) 17 | .on('error', util.log) 18 | .pipe(gulp.dest('./static/favicon')); 19 | }); 20 | 21 | gulp.task('less', function() { 22 | return gulp.src('./frontend/less/*.less') 23 | .pipe(sourcemaps.init()) 24 | .pipe(less()) 25 | .pipe(sourcemaps.write('.')) 26 | .pipe(gulp.dest('./static')); 27 | }); 28 | 29 | gulp.task('build', ['less', 'favicons', 'images']); 30 | 31 | gulp.task('watch', function() { 32 | livereload.listen(); 33 | gulp.watch('./frontend/**', ['build']); 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 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. -------------------------------------------------------------------------------- /templates/help.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout.jinja" %} 2 | 3 | {% block content %} 4 | 5 | {% include "short-header.jinja" %} 6 | 7 |
8 |
9 |
10 |
11 |

12 | Getting help for Clippingsbot: 13 |

14 | 15 |
    16 |
  • 17 | If you have Clippingsbot installed, you can use 18 | 19 | /clippingsbot feedback <message> 20 | to send us a note. 21 |
  • 22 |
  • 23 | You can join the 24 | Skyliner support Slack and ping @mcfunley. 25 |
  • 26 |
  • 27 | You can email Skyliner support. 28 |
  • 29 |
30 | 31 |
32 |
33 |
34 |
35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | ADD . . 3 | 4 | ENV BUILD_DEPS="postgresql-dev build-base wget yarn" 5 | ENV RUNTIME_DEPS="supervisor bash openjdk8-jre-base libpq" 6 | ENV FLYWAY_VERSION=4.1.1 7 | 8 | # testing apk repo is currently needed for yarn. 9 | RUN echo -e 'https://dl-cdn.alpinelinux.org/alpine/edge/testing' >> /etc/apk/repositories && \ 10 | apk update && \ 11 | apk add $BUILD_DEPS $RUNTIME_DEPS && \ 12 | 13 | # flyway 14 | mkdir /opt && \ 15 | wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}.tar.gz | tar -xzf- -C /opt && \ 16 | mv /opt/flyway-${FLYWAY_VERSION} /opt/flyway && \ 17 | 18 | # frontend 19 | yarn install && \ 20 | yarn global add gulp-cli && \ 21 | gulp build && \ 22 | 23 | # python 24 | pip install -r ./requirements.txt && \ 25 | python -m unittest -v test && \ 26 | 27 | apk del $BUILD_DEPS && \ 28 | rm -rf /tmp/* /var/cache/* 29 | 30 | # This is to protect against load balancer keep-alive timeouts; see 31 | # https://github.com/benoitc/gunicorn/issues/1194 and 32 | # https://serverfault.com/questions/782022/keepalive-setting-for-gunicorn-behind-elb-without-nginx 33 | ENV PYTHONUNBUFFERED 1 34 | 35 | CMD ["/usr/bin/supervisord", "-c", "supervisord.conf"] 36 | -------------------------------------------------------------------------------- /frontend/less/includes/reset.less: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /test/test_command.py: -------------------------------------------------------------------------------- 1 | from bot import command 2 | import flask 3 | import os 4 | from unittest import TestCase 5 | from unittest.mock import Mock, patch 6 | from .util import patch_env 7 | 8 | 9 | class RunTest(TestCase): 10 | @patch_env({ 11 | 'SLACK_VERIFICATION_TOKEN': 'foo' 12 | }) 13 | def test_missing_token(self): 14 | with patch.object(flask, 'request', Mock(form={})): 15 | self.assertEqual(('Forbidden', 403), command.run()) 16 | 17 | @patch_env({ 18 | 'SLACK_VERIFICATION_TOKEN': 'foo' 19 | }) 20 | def test_wrong_token(self): 21 | with patch.object(flask, 'request', Mock(form={'token': 'xxx'})): 22 | self.assertEqual(('Forbidden', 403), command.run()) 23 | 24 | @patch_env({ 25 | 'SLACK_VERIFICATION_TOKEN': 'foo' 26 | }) 27 | def test_missing_text(self): 28 | with patch.object(flask, 'request', Mock(form={'token': 'foo'})): 29 | self.assertEqual(('Bad request', 400), command.run()) 30 | 31 | 32 | class ParseTest(TestCase): 33 | def test_trimmed(self): 34 | with patch.object(flask, 'request', Mock(form={'text': ' foo bar '})): 35 | self.assertEqual(('foo', ['bar']), command.parse()) 36 | 37 | def test_lowercase(self): 38 | with patch.object(flask, 'request', Mock(form={'text': ' FOO BAR '})): 39 | self.assertEqual(('foo', ['BAR']), command.parse()) 40 | -------------------------------------------------------------------------------- /test/test_app.py: -------------------------------------------------------------------------------- 1 | from bot import app 2 | from unittest import TestCase 3 | from unittest.mock import Mock, patch 4 | from .util import patch_env 5 | 6 | 7 | class CanonicalRedirectTest(TestCase): 8 | @patch_env({}) 9 | def test_not_configured(self): 10 | self.assertIsNone(app.redirect_canonical_host()) 11 | 12 | @patch_env({ 'CANONICAL_HOST': 'https://foo.com' }) 13 | def test_wrong_host(self): 14 | with patch.object(app, 'request', Mock(url='https://bar.com/thing?x=1')): 15 | r = app.redirect_canonical_host() 16 | self.assertEqual(r.location, 'https://foo.com/thing?x=1') 17 | self.assertEqual(r.status_code, 303) 18 | 19 | @patch_env({ 'CANONICAL_HOST': 'https://foo.com' }) 20 | def test_wrong_proto(self): 21 | with patch.object(app, 'request', Mock(url='http://foo.com/thing?x=1')): 22 | r = app.redirect_canonical_host() 23 | self.assertEqual(r.location, 'https://foo.com/thing?x=1') 24 | self.assertEqual(r.status_code, 303) 25 | 26 | @patch_env({ 'CANONICAL_HOST': 'https://www.foo.com' }) 27 | def test_missing_www(self): 28 | with patch.object(app, 'request', Mock(url='https://foo.com/thing?x=1')): 29 | r = app.redirect_canonical_host() 30 | self.assertEqual(r.location, 'https://www.foo.com/thing?x=1') 31 | self.assertEqual(r.status_code, 303) 32 | 33 | @patch_env({ 'CANONICAL_HOST': 'https://www.foo.com' }) 34 | def test_exempt_health(self): 35 | with patch.object(app, 'request', Mock(url='https://foo.com/health')): 36 | self.assertIsNone(app.redirect_canonical_host()) 37 | -------------------------------------------------------------------------------- /bot/oauth.py: -------------------------------------------------------------------------------- 1 | from bot import team, monitor 2 | import os 3 | import sys 4 | import flask 5 | import requests 6 | from urllib.parse import urlencode 7 | 8 | slack_scope = 'chat:write:bot,commands' 9 | 10 | def authorize_url(): 11 | params = { 12 | 'client_id': os.getenv('SLACK_CLIENT_ID'), 13 | 'redirect_uri': os.getenv('CALLBACK_URL'), 14 | 'scope': slack_scope, 15 | 'state': flask.session['id'], 16 | } 17 | return 'https://slack.com/oauth/authorize?%s' % urlencode(params) 18 | 19 | def callback(): 20 | req = flask.request 21 | if flask.session['id'] != req.args.get('state'): 22 | monitor.notify('Session ID mismatch in oauth callback: %s != %s', 23 | flask.session['id'], req.args.get('state')) 24 | return flask.redirect('/?ref=bad-oauth-state') 25 | 26 | if req.args.get('error') == 'access_denied': 27 | return flask.redirect('/?ref=oauth-denied') 28 | 29 | r = requests.get('https://slack.com/api/oauth.access', params={ 30 | 'code': req.args.get('code'), 31 | 'redirect_uri': os.getenv('CALLBACK_URL'), 32 | 'client_id': os.getenv('SLACK_CLIENT_ID'), 33 | 'client_secret': os.getenv('SLACK_CLIENT_SECRET'), 34 | }) 35 | 36 | if r.status_code != 200: 37 | return flask.redirect('/?ref=oauth-access-failed') 38 | 39 | data = r.json() 40 | if not data['ok']: 41 | vals = ','.join(['%s=%s' % (k, v) for k, v in data.items()]) 42 | monitor.notify('oauth was not ok: %s' % vals) 43 | return flask.redirect('/?ref=oauth-access-not-ok') 44 | 45 | team.save(data) 46 | monitor.notify('Authorized team: %s (%s)' % ( 47 | data['team_name'], data['team_id'])) 48 | return flask.redirect('/installed?team-id=%s' % data['team_id']) 49 | -------------------------------------------------------------------------------- /migrations/V2017_02_16_19_58_34__init.sql: -------------------------------------------------------------------------------- 1 | -- -*- sql-dialect: postgres; -*- 2 | 3 | -- teams get created when the button is clicked. 4 | -- they add patterns with a /command 5 | -- we periodically crawl, and find mentions of patterns. 6 | -- each team subscribed to a pattern gets a notification when it's mentioned 7 | 8 | create table clippingsbot.teams ( 9 | team_id text primary key, 10 | access_token text not null, 11 | user_id text not null, 12 | team_name text not null, 13 | scope text not null 14 | ); 15 | 16 | create table clippingsbot.patterns ( 17 | pattern_id bigserial not null primary key, 18 | pattern text not null, 19 | created timestamp with time zone not null default current_timestamp 20 | ); 21 | 22 | create unique index idx_pattern_lower_unique on clippingsbot.patterns (lower(pattern)); 23 | 24 | create table clippingsbot.team_patterns ( 25 | team_id text not null references clippingsbot.teams (team_id), 26 | pattern_id bigint not null references clippingsbot.patterns (pattern_id), 27 | display_pattern text not null, 28 | 29 | created timestamp with time zone not null default current_timestamp, 30 | primary key (team_id, pattern_id) 31 | ); 32 | 33 | create index idx_team_patterns_sorted 34 | on clippingsbot.team_patterns (team_id, created desc); 35 | 36 | create table clippingsbot.mentions ( 37 | pattern_id bigint not null references clippingsbot.patterns (pattern_id), 38 | discussion_url text not null, 39 | reference_url text, 40 | created timestamp with time zone not null default current_timestamp, 41 | primary key (pattern_id, discussion_url) 42 | ); 43 | 44 | create table clippingsbot.notifications ( 45 | team_id text not null references clippingsbot.teams (team_id), 46 | pattern_id bigint not null references clippingsbot.patterns (pattern_id), 47 | discussion_url text not null, 48 | 49 | created timestamp with time zone not null default current_timestamp, 50 | 51 | primary key (team_id, pattern_id, discussion_url) 52 | ); 53 | -------------------------------------------------------------------------------- /bot/team.py: -------------------------------------------------------------------------------- 1 | from bot import db 2 | 3 | def save(data): 4 | sql = """ 5 | insert into clippingsbot.teams ( 6 | team_id, access_token, user_id, team_name, scope 7 | ) values ( 8 | :team_id, :access_token, :user_id, :team_name, :scope 9 | ) on conflict (team_id) do update 10 | set scope = excluded.scope, 11 | access_token = excluded.access_token, 12 | user_id = excluded.user_id, 13 | team_name = excluded.team_name 14 | 15 | returning team_id 16 | """ 17 | return db.scalar(sql, **data) 18 | 19 | 20 | def find(team_id): 21 | return db.find_one( 22 | 'select * from clippingsbot.teams where team_id = :team_id', 23 | team_id = team_id) 24 | 25 | 26 | def watch(team, channel_id, pattern, pattern_id): 27 | sql = """ 28 | insert into clippingsbot.team_patterns ( 29 | team_id, channel_id, pattern_id, display_pattern 30 | ) 31 | values (:team_id, :channel_id, :pattern_id, :pattern) 32 | on conflict (team_id, channel_id, pattern_id) do nothing 33 | """ 34 | db.execute( 35 | sql, team_id=team['team_id'], channel_id=channel_id, 36 | pattern_id=pattern_id, pattern=pattern 37 | ) 38 | 39 | 40 | def find_patterns(team, channel_id): 41 | sql = """ 42 | select * from 43 | clippingsbot.team_patterns 44 | where team_id = :team_id and channel_id = :channel_id 45 | """ 46 | return db.find(sql, team_id=team['team_id'], channel_id=channel_id) 47 | 48 | 49 | def count_other_channel_patterns(team, channel_id): 50 | sql = """ 51 | select count(*) 52 | from clippingsbot.team_patterns 53 | where team_id = :team_id and channel_id != :channel_id 54 | """ 55 | return db.scalar(sql, team_id=team['team_id'], channel_id=channel_id) 56 | 57 | 58 | def count_patterns(team): 59 | sql = """ 60 | select count(*) from clippingsbot.team_patterns where team_id = :team_id 61 | """ 62 | return db.scalar(sql, team_id=team['team_id']) 63 | 64 | 65 | def stop(team, channel_id, pattern): 66 | sql = """ 67 | delete from clippingsbot.team_patterns 68 | where team_id = :team_id and channel_id = :channel_id 69 | and lower(display_pattern) = lower(:pattern) 70 | """ 71 | db.execute(sql, team_id=team['team_id'], channel_id=channel_id, 72 | pattern=pattern) 73 | -------------------------------------------------------------------------------- /templates/index.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout.jinja" %} 2 | 3 | {% block head %} 4 | 5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |
11 |
12 | 13 |

14 | Clippingsbot 15 | 16 | by Skyliner 17 | 18 |

19 | 20 |

Hacker News notifications for Slack

21 | 22 |
23 | 24 | Add to Slack 25 | 26 |
27 | 28 |

29 | 30 |
31 |
32 |
33 |
34 | 35 |
36 | 38 |
39 | 40 |
41 |
42 |
43 |
44 | 46 |
47 |
48 |
49 |
50 | 51 |
52 |
53 |
54 |
55 |

56 | Maybe you're too busy to watch the tech press for your writing, but 57 | you'd still like to know if people are talking about it. 58 |

59 |

60 | Maybe you're the kind of person who would rather build an entire 61 | Slackbot from scratch than look at Hacker News unnecessarily. 62 |

63 |

64 | Either way, Clippingsbot has your back. 65 |

66 |
67 |
68 |
69 |
70 | {% endblock %} 71 | -------------------------------------------------------------------------------- /bot/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from bot import oauth, command 3 | from flask import ( 4 | Flask, render_template, request, jsonify, session, redirect, 5 | send_file, Response 6 | ) 7 | import os 8 | import requests 9 | import uuid 10 | from urllib.parse import urlparse, urlunparse 11 | from werkzeug.contrib.fixers import ProxyFix 12 | 13 | app = Flask('clippingsbot') 14 | app.wsgi_app = ProxyFix(app.wsgi_app) 15 | app.secret_key = os.getenv('SECRET_KEY') 16 | app.config['TEMPLATES_AUTO_RELOAD'] = bool( 17 | os.getenv('TEMPLATES_AUTO_RELOAD', False)) 18 | 19 | 20 | @app.before_request 21 | def set_session_id(): 22 | if not session.get('id', None): 23 | session['id'] = str(uuid.uuid4()) 24 | 25 | 26 | def is_healthcheck(): 27 | request_url = urlparse(request.url) 28 | return request_url.path == '/health' 29 | 30 | 31 | @app.before_request 32 | def redirect_canonical_host(): 33 | canonical = os.getenv('CANONICAL_HOST', None) 34 | if not canonical: 35 | return 36 | 37 | if is_healthcheck(): 38 | return 39 | 40 | u = list(urlparse(request.url)) 41 | c = list(urlparse(canonical)) 42 | if u[0:2] != c[0:2]: 43 | u[0:2] = c[0:2] 44 | return redirect(urlunparse(u), code=303) 45 | 46 | 47 | @app.before_request 48 | def force_basic_auth(): 49 | user = os.getenv('BASIC_AUTH_USERNAME', None) 50 | pw = os.getenv('BASIC_AUTH_PASSWORD', None) 51 | 52 | if not user: 53 | return 54 | 55 | if is_healthcheck(): 56 | return 57 | 58 | auth = request.authorization 59 | if not auth or not (auth.username == user and auth.password == pw): 60 | return Response( 61 | 'Authorization required', 401, { 62 | 'WWW-Authenticate': 'Basic realm="Login Required"' 63 | }) 64 | 65 | 66 | @app.route('/') 67 | def index(): 68 | return render_template('index.jinja', **{ 69 | 'authorize_url': oauth.authorize_url() 70 | }) 71 | 72 | @app.route('/health') 73 | def health(): 74 | return 'ok' 75 | 76 | @app.route('/oauth') 77 | def oauth_callback(): 78 | return oauth.callback() 79 | 80 | @app.route('/installed') 81 | def installed(): 82 | return render_template('installed.jinja') 83 | 84 | @app.route('/command', methods=['POST']) 85 | def cmd(): 86 | return command.run() 87 | 88 | @app.route('/favicon.ico', methods=['GET']) 89 | def favicon(): 90 | return send_file('static/favicon/favicon.ico', mimetype='image/x-icon') 91 | 92 | @app.route('/help', methods=['GET']) 93 | def help(): 94 | return render_template('help.jinja') 95 | 96 | @app.route('/privacy', methods=['GET']) 97 | def privacy(): 98 | return render_template('privacy.jinja') 99 | -------------------------------------------------------------------------------- /bot/notify.py: -------------------------------------------------------------------------------- 1 | from bot import db, team, monitor 2 | import requests 3 | from slackclient import SlackClient 4 | 5 | 6 | pending_sql = """ 7 | select m.pattern_id, m.feed, m.title, m.comments_url, m.link_url, 8 | tp.team_id, tp.channel_id, tp.display_pattern 9 | from clippingsbot.mentions m 10 | left join clippingsbot.team_patterns tp on m.pattern_id = tp.pattern_id 11 | where team_id is not null and not exists ( 12 | select 1 13 | from clippingsbot.notifications n 14 | where n.team_id = tp.team_id 15 | and n.pattern_id = tp.pattern_id 16 | and n.feed = m.feed 17 | and n.comments_url = m.comments_url 18 | and n.channel_id = tp.channel_id 19 | ) 20 | order by m.created asc limit 50; 21 | """ 22 | 23 | 24 | def message(pending_notification): 25 | if pending_notification['feed'] == 'homepage': 26 | fmt = ('A mention of `%s` made the Hacker News homepage: \n' 27 | '<%s|%s> [<%s|comments>]') 28 | elif pending_notification['feed'] == 'newest': 29 | fmt = ('New submission mentioning `%s` on Hacker News: \n' 30 | '<%s|%s> [<%s|comments>]') 31 | 32 | return fmt % ( 33 | pending_notification['display_pattern'], 34 | pending_notification['link_url'], 35 | pending_notification['title'], 36 | pending_notification['comments_url'], 37 | ) 38 | 39 | 40 | def post(pending_notification): 41 | t = team.find(pending_notification['team_id']) 42 | c = SlackClient(t['access_token']) 43 | c.api_call( 44 | 'chat.postMessage', 45 | channel=pending_notification['channel_id'], 46 | text=message(pending_notification), 47 | unfurl_links=False 48 | ) 49 | 50 | 51 | def record(pending_notification): 52 | sql = """ 53 | insert into clippingsbot.notifications ( 54 | team_id, pattern_id, channel_id, feed, comments_url, link_url 55 | ) values ( 56 | :team_id, :pattern_id, :channel_id, :feed, :comments_url, :link_url 57 | ) 58 | on conflict (team_id, pattern_id, channel_id, feed, comments_url) 59 | do update set created = current_timestamp 60 | """ 61 | db.execute( 62 | sql, 63 | team_id=pending_notification['team_id'], 64 | pattern_id=pending_notification['pattern_id'], 65 | channel_id=pending_notification['channel_id'], 66 | feed=pending_notification['feed'], 67 | link_url=pending_notification['link_url'], 68 | comments_url=pending_notification['comments_url'], 69 | ) 70 | 71 | def run(): 72 | sent = 0 73 | for pending_notification in db.find(pending_sql): 74 | sent += 1 75 | post(pending_notification) 76 | record(pending_notification) 77 | 78 | if sent > 0: 79 | monitor.notify('notifier sent %s notifications' % sent) 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clippingsbot 2 | 3 | Clippingsbot is a Slack bot that watches Hacker News submissions for mentions of words and phrases. 4 | 5 | Clippingsbot is a project of [Skyliner](https://www.skyliner.io), a deployment platform for AWS. You can use Skyliner to build your own Slack bot like this one. 6 | 7 | ## Architecture 8 | 9 | The Slack command endpoint is a simple flask app. End users of the bot run `/clippingsbot ` in their slack, and that is routed by `bot/app.py` to one of the implementations in `bot/commands.py`. 10 | 11 | There is also a worker process (in `bot/worker.py`). The worker does two things: 12 | 13 | * It crawls for new mentions of watched phrases. 14 | * It sends notifications to end users. 15 | 16 | The two stages of notification are separated. Mentions found when crawling are stored in an RDS postgres database, and then picked up and turned into notifications in a second pass. 17 | 18 | The worker and the web app are run together in a single Docker container using [supervisor](http://supervisord.org/). This is done since they share code, and therefore should have a single deploy button. 19 | 20 | Clippingsbot uses [Cloudwatch Events](http://docs.aws.amazon.com/AmazonCloudWatch/latest/events/WhatIsCloudWatchEvents.html) combined with [SQS](https://aws.amazon.com/sqs/) to run scheduled jobs (i.e., a distributed cron). An event enqueues work in SQS, and the worker process loops in an endless SQS poll dequeueing and dispatching it. 21 | 22 | ## Dev setup 23 | 24 | ``` 25 | brew install flyway pyenv pyenv-virtualenv postgresql yarn nodejs 26 | 27 | pyenv virtualenv 3.6.0 clippingsbot 28 | pyenv activate clippingsbot 29 | pip install -r requirements.txt 30 | 31 | yarn install 32 | yarn global add gulp-cli 33 | 34 | createdb clippingsbot 35 | bin/migrate 36 | ``` 37 | 38 | Make a `.env` file, and put environment variables in it (`X="Y"` syntax). You can grep the codebase for `os.getenv` to get a current list of the ones you need. 39 | 40 | Running the dev server: 41 | 42 | ``` 43 | bin/dev 44 | ``` 45 | 46 | Also run the frontend (in a different console): 47 | 48 | ``` 49 | gulp build && gulp watch 50 | ``` 51 | 52 | To get a dev repl: 53 | 54 | ``` 55 | bin/repl 56 | ``` 57 | 58 | ### Tests 59 | 60 | To run the tests: 61 | 62 | ``` 63 | python -m unittest -v test 64 | ``` 65 | 66 | ### Coding 67 | 68 | Slack doesn't have a great way, short of creating multiple bots, to develop a bot that's already in production. For now, you can do most things from the repl. 69 | 70 | ``` 71 | $ bin/repl 72 | >>> from bot import crawl 73 | >>> crawl.run() 74 | 75 | >>> from bot import notify 76 | >>> notify.run() 77 | ``` 78 | 79 | 80 | ## AWS Credentials 81 | 82 | **Do not write code that refers to `AWS_SECRET_ACCESS_KEY` and `AWS_ACCESS_KEY_ID`**. 83 | 84 | Use [credentials files](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-config-files). The `AWS_PROFILE` env var with [named profiles](http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html#cli-multiple-profiles) is handy if you are juggling multiple accounts. 85 | -------------------------------------------------------------------------------- /templates/layout.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block title %}Clippingsbot :: Hacker News Notifications for Slack{% endblock %} 5 | 6 | 7 | {% block head %}{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | {% block content %}{% endblock %} 40 | 41 |
42 |
43 |
44 |
45 |

46 | Clippingsbot is a project by 47 | Skyliner, 48 | an AWS continuous delivery platform. You can launch your own Slack 49 | bot on Skyliner with a few clicks. 50 |

51 | 65 |
66 |
67 |
68 |
69 | 70 |
71 | 72 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /bot/crawl.py: -------------------------------------------------------------------------------- 1 | from bot import db, patterns, mentions, monitor 2 | from collections import defaultdict 3 | import feedparser 4 | import re 5 | from stop_words import get_stop_words 6 | 7 | 8 | stop_words = set(get_stop_words('en')) 9 | 10 | 11 | def tokenize(fulltext): 12 | fulltext = re.sub('[^\w]', ' ', fulltext).lower() 13 | return re.split('\s+', fulltext) 14 | 15 | 16 | def tokenize_entrydict(entrydict): 17 | return tokenize(' '.join([entrydict['title'], entrydict['link']])) 18 | 19 | 20 | def extract_terms(entrydict): 21 | return set(tokenize_entrydict(entrydict)) 22 | 23 | 24 | def match(terms, e): 25 | phrase = ' '.join(terms) 26 | tokens = e.tokens() 27 | 28 | # exact match 29 | if phrase in ' '.join(tokens): 30 | return True 31 | 32 | # stopwords match 33 | if phrase in ' '.join([t for t in tokens if t not in stop_words]): 34 | return True 35 | 36 | return False 37 | 38 | 39 | class Entry(object): 40 | def __init__(self, original, terms, feed_name): 41 | self.original = original 42 | self.terms = terms 43 | self.feed_name = feed_name 44 | 45 | def tokens(self): 46 | return tokenize_entrydict(self.original) 47 | 48 | def to_mention(self, phrase): 49 | return { 50 | 'phrase': phrase, 51 | 'feed': self.feed_name, 52 | 'title': self.original['title'], 53 | 'comments_url': self.original['comments'], 54 | 'link_url': self.original['link'], 55 | } 56 | 57 | 58 | class InvertedIndex(object): 59 | def __init__(self, feeds): 60 | self.feeds = feeds 61 | 62 | # term -> posting list of entries 63 | self.index = defaultdict(set) 64 | 65 | def build_index(self): 66 | for name, f in self.feeds.items(): 67 | self.add_feed(name, f) 68 | 69 | def add_feed(self, name, feed): 70 | for e in feed['entries']: 71 | terms = extract_terms(e) 72 | entry = Entry(e, terms, name) 73 | 74 | for t in terms: 75 | self.index[t].add(entry) 76 | 77 | def search(self, phrase): 78 | terms = tokenize(phrase) 79 | postings = None 80 | 81 | # find entries matching all terms in phrase 82 | for t in terms: 83 | if not postings: 84 | postings = self.index[t].copy() 85 | else: 86 | postings &= self.index[t] 87 | 88 | if not len(postings): 89 | return [] 90 | 91 | # limit to exact phrases 92 | for e in postings.copy(): 93 | if not match(terms, e): 94 | postings.remove(e) 95 | 96 | return [e.to_mention(phrase) for e in postings] 97 | 98 | 99 | def build_index(): 100 | idx = InvertedIndex({ 101 | 'newest': feedparser.parse('http://hnrss.org/newest'), 102 | 'homepage': feedparser.parse('https://news.ycombinator.com/rss'), 103 | }) 104 | idx.build_index() 105 | return idx 106 | 107 | 108 | def run(): 109 | idx = build_index() 110 | 111 | found = 0 112 | 113 | for p in patterns.find_all(): 114 | for mention in idx.search(p['pattern']): 115 | print('Found mention of "%s" on %s (%s)' % ( 116 | p['pattern'], mention['link_url'], mention['feed'] 117 | )) 118 | created = mentions.save(p, mention) 119 | if created: 120 | found += 1 121 | 122 | if found > 0: 123 | monitor.notify('Crawler found %s mentions.' % found) 124 | -------------------------------------------------------------------------------- /frontend/img/skyliner-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo-inverse 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /bot/command.py: -------------------------------------------------------------------------------- 1 | from bot import team, patterns, monitor 2 | import flask 3 | import os 4 | import re 5 | 6 | usage = """*Usage*: 7 | `/clippingsbot [command] [arguments]` 8 | 9 | *Commands*: 10 | ``` 11 | help Display this help. 12 | watch Watch for mentions of a phrase. 13 | stop Stop watching for mentions of a phrase. 14 | list List phrases currently being watched. 15 | feedback Send feedback or bug reports about clippingsbot. 16 | ``` 17 | 18 | *Examples*: 19 | ``` 20 | /clippingsbot watch foobar.com 21 | /clippingsbot watch several words 22 | /clippingsbot stop foobar.com 23 | ``` 24 | 25 | Want to build your own Slackbot? Try . 26 | """ 27 | 28 | 29 | def show_help(): 30 | return flask.jsonify({ 31 | 'response_type': 'in_channel', 32 | 'text': usage 33 | }) 34 | 35 | 36 | def notify(msg): 37 | monitor.notify('%s (user=%s, team=%s (%s))' % ( 38 | msg, 39 | flask.request.form.get('user_name', None), 40 | flask.request.form.get('team_domain', None), 41 | flask.request.form.get('team_id', None), 42 | )) 43 | 44 | 45 | def watch(phrase): 46 | if not len(phrase): 47 | return 'Sorry, I need a phrase. Usage: `/clippingsbot watch `.' 48 | 49 | if len(phrase) < 4: 50 | return 'Sorry, the phrase must be four or more characters.' 51 | 52 | team_id = flask.request.form.get('team_id', None) 53 | if not team_id: 54 | return 'Bad request', 400 55 | 56 | channel_id = flask.request.form.get('channel_id', None) 57 | if not channel_id: 58 | return 'Bad request', 400 59 | 60 | t = team.find(team_id) 61 | if not t: 62 | return 'Bad request', 400 63 | 64 | if team.count_patterns(t) >= 5: 65 | notify('too many patterns') 66 | return ('Sorry, you can watch a maximum of five phrases for now. ' 67 | 'We have noted your interest though and will address this if ' 68 | 'it is widespread.') 69 | 70 | pattern_id = patterns.save(phrase) 71 | team.watch(t, channel_id, phrase, pattern_id) 72 | notify('watched a phrase') 73 | return ("Ok, I'm watching for mentions of the phrase `%s` " 74 | "in this channel." % phrase) 75 | 76 | 77 | def stop(phrase): 78 | if not len(phrase): 79 | return 'Sorry, I need a phrase. Usage: `/clippingsbot watch `.' 80 | 81 | team_id = flask.request.form.get('team_id', None) 82 | if not team_id: 83 | return 'Bad request', 400 84 | 85 | channel_id = flask.request.form.get('channel_id', None) 86 | if not channel_id: 87 | return 'Bad request', 400 88 | 89 | t = team.find(team_id) 90 | if not t: 91 | return 'Bad request', 400 92 | 93 | team.stop(t, channel_id, phrase) 94 | notify('stopped watching a phrase') 95 | return ("Ok, I won't alert you about mentions of `%s` " 96 | "in this channel." % phrase) 97 | 98 | 99 | def parse(): 100 | cmd, *args = re.split('\s+', flask.request.form['text'].strip()) 101 | return cmd.lower(), args 102 | 103 | 104 | def list_patterns(): 105 | team_id = flask.request.form.get('team_id', None) 106 | if not team_id: 107 | return 'Bad request', 400 108 | 109 | channel_id = flask.request.form.get('channel_id', None) 110 | if not channel_id: 111 | return 'Bad request', 400 112 | 113 | t = team.find(team_id) 114 | if not t: 115 | return 'Bad request', 400 116 | 117 | channel_id = flask.request.form.get('channel_id', None) 118 | if not channel_id: 119 | return 'Bad request', 400 120 | 121 | channel_watches = list(team.find_patterns(t, channel_id)) 122 | other_channels_count = team.count_other_channel_patterns(t, channel_id) 123 | 124 | notify('listed patterns') 125 | 126 | if other_channels_count > 0: 127 | if other_channels_count > 1: 128 | other_channels_msg = ('I am watching for %s phrases in other ' 129 | 'channels.' % other_channels_count) 130 | else: 131 | other_channels_msg = ('I am watching for one phrase in ' 132 | 'another channel.') 133 | 134 | if not len(channel_watches): 135 | if other_channels_count == 0: 136 | return 'I am not currently watching for any phrases.' 137 | else: 138 | return other_channels_msg 139 | 140 | phrase_list = ','.join(['`%s`' % p['display_pattern'] 141 | for p in channel_watches]) 142 | msg = ('I am watching for mentions of the following phrases ' 143 | 'in this channel: %s.' % phrase_list) 144 | if other_channels_count == 0: 145 | return msg 146 | 147 | return '%s %s' % (msg, other_channels_msg) 148 | 149 | 150 | def feedback(text): 151 | notify('Feedback: "%s"' % text) 152 | return ("Thanks! We've got your feedback. Make sure you include " 153 | "your email if you'd like a response.") 154 | 155 | 156 | def run(): 157 | request = flask.request 158 | tok = os.getenv('SLACK_VERIFICATION_TOKEN') 159 | if request.form.get('token', None) != tok: 160 | return 'Forbidden', 403 161 | 162 | if not 'text' in request.form: 163 | return 'Bad request', 400 164 | 165 | cmd, args = parse() 166 | if cmd == 'help': 167 | notify('showed help') 168 | return show_help() 169 | elif cmd == 'watch': 170 | return watch(' '.join(args)) 171 | elif cmd == 'stop': 172 | return stop(' '.join(args)) 173 | elif cmd == 'list': 174 | return list_patterns() 175 | elif cmd == 'feedback': 176 | return feedback(' '.join(args)) 177 | 178 | notify('unknown command') 179 | return show_help() 180 | -------------------------------------------------------------------------------- /frontend/less/includes/fluidable.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Fluidable Grid System 1.3 3 | * 4 | * Creator: Andri Sigurðsson 5 | * Site: http://fluidable.com 6 | * Date: 31.05.2016 7 | */ 8 | 9 | // 10 | // Config 11 | // 12 | 13 | // Grid 14 | 15 | @columns: 12; 16 | @gutterWidth: 36px; 17 | 18 | // Column size 19 | 20 | @columnWidth: 100%/@columns; 21 | 22 | // Break-points 23 | 24 | @screenTablet: 768px; 25 | @screenDesktop: 992px; 26 | @screenLarge: 1200px; 27 | 28 | // 29 | // Mobile and up 30 | // 31 | 32 | [class*="col-fixed-"] { 33 | float: left; 34 | width: 100%; 35 | } 36 | 37 | .col-group { 38 | padding: 0 @gutterWidth; 39 | .clear(); 40 | } 41 | 42 | .col-group .col-group { 43 | padding: 0; 44 | } 45 | 46 | .col-group [class*="col-"] { 47 | min-height: 1px; 48 | -webkit-box-sizing: border-box; 49 | -moz-box-sizing: border-box; 50 | box-sizing: border-box; 51 | } 52 | 53 | .col-group [class*="push-"], 54 | .col-group [class*="pull-"] { 55 | position: relative; 56 | } 57 | 58 | // Columns 59 | 60 | .fluidable (@index) when (@index > 0) { 61 | .col-mb-@{index} { 62 | width: @columnWidth * @index; 63 | float: left; 64 | padding-right: @gutterWidth / 2; 65 | padding-left: @gutterWidth / 2; 66 | } 67 | .fluidable(@index - 1); 68 | } 69 | .fluidable (0) {} 70 | .fluidable (@columns); 71 | 72 | 73 | // 74 | // Tablet and up 75 | // 76 | 77 | @media (min-width: @screenTablet) { 78 | 79 | .container { 80 | max-width: @screenTablet - (@gutterWidth * 2); 81 | .center-block(); 82 | } 83 | 84 | // Columns 85 | 86 | .fluidable (@index) when (@index > 0) { 87 | .col-@{index} { 88 | padding-left: @gutterWidth / 2; 89 | padding-right: @gutterWidth / 2; 90 | width: @columnWidth * @index; 91 | float: left; 92 | } 93 | .fluidable(@index - 1); 94 | } 95 | .fluidable (0) {} 96 | .fluidable (@columns); 97 | 98 | 99 | // Offset 100 | 101 | .offset (@index) when (@index > -1) { 102 | .col-offset-@{index} { 103 | margin-left: @columnWidth * @index; 104 | } 105 | .offset(@index - 1); 106 | } 107 | .offset (0) {} 108 | .offset (@columns); 109 | 110 | 111 | // Pull 112 | 113 | .pull (@index) when (@index > -1) { 114 | .col-pull-@{index} { 115 | right: @columnWidth * @index; 116 | } 117 | .pull(@index - 1); 118 | } 119 | .pull (0) {} 120 | .pull (@columns); 121 | 122 | 123 | // Push 124 | 125 | .push (@index) when (@index > -1) { 126 | .col-push-@{index} { 127 | left: @columnWidth * @index; 128 | } 129 | .push(@index - 1); 130 | } 131 | .push (0) {} 132 | .push (@columns); 133 | 134 | 135 | // Groups 136 | 137 | .col-group { 138 | margin-right: @gutterWidth / -2; 139 | margin-left: @gutterWidth / -2; 140 | padding: 0; 141 | .clear(); 142 | } 143 | 144 | } 145 | 146 | // 147 | // Desktop and up 148 | // 149 | 150 | @media (min-width: @screenDesktop) { 151 | 152 | .container { 153 | max-width: @screenDesktop - (@gutterWidth * 2); 154 | .center-block(); 155 | } 156 | 157 | // Columns 158 | 159 | .fluidable (@index) when (@index > 0) { 160 | .col-dt-@{index} { 161 | padding-left: @gutterWidth / 2; 162 | padding-right: @gutterWidth / 2; 163 | width: @columnWidth * @index; 164 | float: left; 165 | } 166 | .fluidable(@index - 1); 167 | } 168 | .fluidable (0) {} 169 | .fluidable (@columns); 170 | 171 | 172 | // Offset 173 | 174 | .offset (@index) when (@index > -1) { 175 | .col-dt-offset-@{index} { 176 | margin-left: @columnWidth * @index; 177 | } 178 | .offset(@index - 1); 179 | } 180 | .offset (0) {} 181 | .offset (@columns); 182 | 183 | 184 | // Pull 185 | 186 | .pull (@index) when (@index > -1) { 187 | .col-dt-pull-@{index} { 188 | right: @columnWidth * @index; 189 | } 190 | .pull(@index - 1); 191 | } 192 | .pull (0) {} 193 | .pull (@columns); 194 | 195 | 196 | // Push 197 | 198 | .push (@index) when (@index > -1) { 199 | .col-dt-push-@{index} { 200 | left: @columnWidth * @index; 201 | } 202 | .push(@index - 1); 203 | } 204 | .push (0) {} 205 | .push (@columns); 206 | 207 | } 208 | 209 | 210 | // 211 | // Large desktop and up 212 | // 213 | 214 | @media (min-width: @screenLarge) { 215 | 216 | .container { 217 | max-width: @screenLarge - (@gutterWidth * 2); 218 | .center-block(); 219 | } 220 | 221 | // Columns 222 | 223 | .fluidable (@index) when (@index > 0) { 224 | .col-ld-@{index} { 225 | padding-left: @gutterWidth / 2; 226 | padding-right: @gutterWidth / 2; 227 | width: @columnWidth * @index; 228 | float: left; 229 | } 230 | .fluidable(@index - 1); 231 | } 232 | .fluidable (0) {} 233 | .fluidable (@columns); 234 | 235 | 236 | // Offset 237 | 238 | .offset (@index) when (@index > -1) { 239 | .col-ld-offset-@{index} { 240 | margin-left: @columnWidth * @index; 241 | } 242 | .offset(@index - 1); 243 | } 244 | .offset (0) {} 245 | .offset (@columns); 246 | 247 | 248 | // Pull 249 | 250 | .pull (@index) when (@index > -1) { 251 | .col-ld-pull-@{index} { 252 | right: @columnWidth * @index; 253 | } 254 | .pull(@index - 1); 255 | } 256 | .pull (0) {} 257 | .pull (@columns); 258 | 259 | 260 | // Push 261 | 262 | .push (@index) when (@index > -1) { 263 | .col-ld-push-@{index} { 264 | left: @columnWidth * @index; 265 | } 266 | .push(@index - 1); 267 | } 268 | .push (0) {} 269 | .push (@columns); 270 | 271 | } 272 | 273 | // 274 | // Fixed aspect ratio columns 275 | // 276 | 277 | .col-fixed-hd, 278 | .col-fixed-landscape, 279 | .col-fixed-square, 280 | .col-fixed-portrait { 281 | position: relative; 282 | } 283 | 284 | .col-fixed-hd:before, 285 | .col-fixed-landscape:before, 286 | .col-fixed-square:before, 287 | .col-fixed-portrait:before { 288 | content: ""; 289 | display: block; 290 | } 291 | 292 | .col-fixed-hd .col-content, 293 | .col-fixed-landscape .col-content, 294 | .col-fixed-square .col-content, 295 | .col-fixed-portrait .col-content { 296 | position: absolute; 297 | top: 0; right: @gutterWidth / 2; bottom: 0; left: @gutterWidth / 2; 298 | padding: 0; 299 | } 300 | 301 | .col-fixed-hd:before { 302 | margin-top: 56.25%; 303 | } 304 | 305 | .col-fixed-landscape:before { 306 | margin-top: 75%; 307 | } 308 | 309 | .col-fixed-square:before { 310 | margin-top: 100%; 311 | } 312 | 313 | .col-fixed-portrait:before { 314 | margin-top: 133.33333333%; 315 | } 316 | 317 | 318 | // 319 | // Other 320 | // 321 | 322 | // Positioning 323 | 324 | .center-block { 325 | margin: 0 auto; 326 | } 327 | 328 | // Clearfix 329 | 330 | .clear { 331 | &:after { 332 | display: table; 333 | clear: both; 334 | content: " "; 335 | } 336 | } -------------------------------------------------------------------------------- /frontend/less/clippingsbot.less: -------------------------------------------------------------------------------- 1 | @import "includes/reset.less"; 2 | @import "includes/fluidable.less"; 3 | 4 | @dark-green: rgba(34, 111, 84, 1); 5 | @light-green: rgba(135, 195, 143, 1); 6 | @tan: rgba(244, 240, 187, 1); 7 | @brown: rgba(67, 41, 31, 1); 8 | @sienna: rgba(186, 63, 29, 1); 9 | @gunmetal: #495762; 10 | 11 | // ------------------------------------------------------------------------------ 12 | // overall 13 | 14 | @footer-height: 220px; 15 | @footer-mb-height: 262px; 16 | 17 | 18 | * { 19 | box-sizing: border-box; 20 | } 21 | 22 | html { 23 | position: relative; 24 | min-height: 100%; 25 | } 26 | 27 | body { 28 | height: 100%; 29 | width: 100%; 30 | 31 | margin-bottom: @footer-mb-height; 32 | @media (min-width: @screenTablet) { 33 | margin-bottom: @footer-height; 34 | } 35 | } 36 | 37 | footer { 38 | position: absolute; 39 | bottom: 0; 40 | 41 | height: @footer-mb-height; 42 | @media (min-width: @screenTablet) { 43 | height: @footer-height; 44 | } 45 | } 46 | 47 | a { 48 | &, &:visited { 49 | color: @tan; 50 | } 51 | &.dark, &.dark:visited { 52 | color: @brown; 53 | } 54 | &:hover, &:focus, &:active { 55 | color: @light-green; 56 | } 57 | &.naked { 58 | color: #fff; 59 | text-decoration: none; 60 | } 61 | } 62 | 63 | 64 | // ------------------------------------------------------------------------------ 65 | // typography 66 | 67 | @h1-size: 80px; 68 | @h1-mb-size: 40px; 69 | 70 | @h2-size: 30px; 71 | @h2-mb-size: 25px; 72 | 73 | body { 74 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; 75 | 76 | } 77 | 78 | h1, h2 { 79 | letter-spacing: 1.4px; 80 | } 81 | 82 | h1 { 83 | font-size: @h1-mb-size; 84 | @media (min-width: @screenTablet) { 85 | font-size: @h1-size; 86 | } 87 | 88 | font-weight: 200; 89 | 90 | small { 91 | font-size: 18px; 92 | } 93 | } 94 | 95 | h2 { 96 | font-size: @h2-mb-size; 97 | @media (min-width: @screenTablet) { 98 | font-size: @h2-size; 99 | } 100 | 101 | font-weight: 100; 102 | } 103 | 104 | @p-font-size: 24px; 105 | @p-mb-font-size: 18px; 106 | 107 | p { 108 | font-size: @p-mb-font-size; 109 | 110 | @media (min-width: @screenTablet) { 111 | font-size: @p-font-size; 112 | font-weight: 300; 113 | letter-spacing: 0.4px; 114 | word-spacing: 1.2px; 115 | line-height: 1.2; 116 | } 117 | 118 | em.italic { 119 | word-spacing: -0.2px; 120 | } 121 | } 122 | 123 | p + p { 124 | margin-top: 25px; 125 | } 126 | 127 | code { 128 | margin-top: 10px; 129 | font-family: Menlo, 'DejaVu Sans Mono', 'Lucida Console', monospace; 130 | font-weight: 200; 131 | padding: 20px; 132 | background-color: #eee; 133 | display: inline-block; 134 | width: 100%; 135 | font-size: 14px; 136 | 137 | @media (min-width: @screenTablet) { 138 | font-size: @p-font-size; 139 | } 140 | 141 | .command { 142 | color: @dark-green; 143 | } 144 | 145 | &.inline-code { 146 | margin: 0; 147 | padding: 3px; 148 | width: initial; 149 | font-size: inherit; 150 | } 151 | } 152 | 153 | // ------------------------------------------------------------------------------ 154 | // util classes 155 | 156 | .mb-hidden-block { 157 | display: none; 158 | @media (min-width: @screenTablet) { 159 | display: block; 160 | } 161 | } 162 | 163 | .tablet-hidden-block { 164 | display: block; 165 | @media (min-width: @screenTablet) { 166 | display: none; 167 | } 168 | } 169 | 170 | .text-center { text-align: center; } 171 | .italic { font-style: italic; } 172 | 173 | .inline-block { display: inline-block; } 174 | .block { display: block; } 175 | 176 | .pos-relative { position: relative; } 177 | .pos-absolute { position: absolute; } 178 | .abs-right { right: 0; } 179 | 180 | .wt300 { font-weight: 300; } 181 | 182 | // ------------------------------------------------------------------------------ 183 | // page 184 | 185 | header, section, footer { 186 | display: table; 187 | width: 100%; 188 | padding: 70px 0; 189 | } 190 | 191 | section#demo-mobile { 192 | padding: 10px; 193 | background-color: #eee; 194 | 195 | img { width: 100%; } 196 | } 197 | 198 | section#demo { 199 | background-color: #eee; 200 | 201 | img { 202 | width: 100%; 203 | border: 1px solid @gunmetal; 204 | } 205 | } 206 | 207 | header { 208 | background-color: @sienna; 209 | color: #fff; 210 | 211 | h1#title { 212 | height: (2 * @h1-mb-size); 213 | small { 214 | top: (@h1-mb-size + 7px); 215 | } 216 | 217 | @media (min-width: @screenTablet) { 218 | height: (2 * @h1-size); 219 | small { 220 | top: (@h1-size + 7px); 221 | } 222 | } 223 | } 224 | 225 | h2 { 226 | margin-top: 40px; 227 | } 228 | 229 | #add-button { 230 | padding: 70px 0 0 0; 231 | } 232 | } 233 | 234 | header.smaller { 235 | padding: 30px 0; 236 | 237 | h1 { 238 | font-size: @h2-mb-size; 239 | @media (min-width: @screenTablet) { 240 | font-size: @h2-size; 241 | } 242 | } 243 | 244 | small#subtitle { 245 | font-size: 14px; 246 | } 247 | } 248 | 249 | footer { 250 | background-color: @gunmetal; 251 | color: #fff; 252 | 253 | p { 254 | font-size: (@p-mb-font-size); 255 | @media (min-width: @screenTablet) { 256 | font-size: (@p-font-size * 0.8); 257 | } 258 | } 259 | } 260 | 261 | #installed-heading { 262 | margin-bottom: 40px; 263 | } 264 | 265 | ul.linklist { 266 | list-style-type: none; 267 | text-align: center; 268 | margin-top: 20px; 269 | 270 | font-size: 12px; 271 | @media (min-width: @screenTablet) { 272 | font-size: 14px; 273 | } 274 | 275 | li { 276 | display: inline; 277 | margin-right: 10px; 278 | 279 | &:last-child { 280 | margin-right: 0; 281 | } 282 | } 283 | } 284 | 285 | ul#help-methods { 286 | list-style-type: disc; 287 | margin-top: 20px; 288 | padding: 20px 40px; 289 | 290 | li { 291 | margin-bottom: 20px; 292 | &:last-child { 293 | margin-bottom: 0; 294 | } 295 | } 296 | } 297 | 298 | #privacy { 299 | h3 { 300 | padding: 20px 0; 301 | font-weight: 500; 302 | } 303 | 304 | h2#privacy-head { 305 | margin-top: 40px; 306 | } 307 | 308 | 309 | p { 310 | font-size: 16px; 311 | } 312 | 313 | ul li, ol li { 314 | margin-bottom: 20px; 315 | &:last-child { 316 | margin: 0; 317 | } 318 | } 319 | 320 | ul, ol { 321 | padding-left: 40px; 322 | } 323 | 324 | ul { 325 | padding: 20px 0 20px 40px; 326 | list-style-type: disc; 327 | } 328 | 329 | ol { 330 | &[type="a"] { 331 | list-style-type: lower-alpha; 332 | } 333 | &[type="i"] { 334 | list-style-type: lower-roman; 335 | } 336 | } 337 | 338 | 339 | } 340 | -------------------------------------------------------------------------------- /templates/privacy.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout.jinja" %} 2 | 3 | {% block content %} 4 | 5 | {% include "short-header.jinja" %} 6 | 7 |
8 |
9 |
10 |
11 |

Construct Software, Inc. Terms of Service and Privacy Policy

12 | 13 |

1. Terms

14 | 15 |

By accessing the website at http://www.clippingsbot.com, you are agreeing to be bound by these terms of service, all applicable laws and regulations, and agree that you are responsible for compliance with any applicable local laws. If you do not agree with any of these terms, you are prohibited from using or accessing this site. The materials contained in this website are protected by applicable copyright and trademark law.

16 | 17 |

2. Use License

18 | 19 |
    20 |
  1. 21 | Permission is granted to temporarily download one copy of the materials (information or software) on Construct Software, Inc.'s website for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not: 22 | 23 |
      24 |
    1. modify or copy the materials;
    2. 25 |
    3. use the materials for any commercial purpose, or for any public display (commercial or non-commercial);
    4. 26 |
    5. attempt to decompile or reverse engineer any software contained on Construct Software, Inc.'s website;
    6. 27 |
    7. remove any copyright or other proprietary notations from the materials; or
    8. 28 |
    9. transfer the materials to another person or "mirror" the materials on any other server.
    10. 29 |
    30 |
  2. 31 |
  3. This license shall automatically terminate if you violate any of these restrictions and may be terminated by Construct Software, Inc. at any time. Upon terminating your viewing of these materials or upon the termination of this license, you must destroy any downloaded materials in your possession whether in electronic or printed format.
  4. 32 |
33 | 34 |

3. Disclaimer

35 | 36 |
    37 |
  1. The materials on Construct Software, Inc.'s website are provided on an 'as is' basis. Construct Software, Inc. makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.
  2. 38 |
  3. Further, Construct Software, Inc. does not warrant or make any representations concerning the accuracy, likely results, or reliability of the use of the materials on its website or otherwise relating to such materials or on any sites linked to this site.
  4. 39 |
40 | 41 |

4. Limitations

42 | 43 |

In no event shall Construct Software, Inc. or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on Construct Software, Inc.'s website, even if Construct Software, Inc. or a Construct Software, Inc. authorized representative has been notified orally or in writing of the possibility of such damage. Because some jurisdictions do not allow limitations on implied warranties, or limitations of liability for consequential or incidental damages, these limitations may not apply to you.

44 | 45 |

5. Accuracy of materials

46 | 47 |

The materials appearing on Construct Software, Inc.'s website could include technical, typographical, or photographic errors. Construct Software, Inc. does not warrant that any of the materials on its website are accurate, complete or current. Construct Software, Inc. may make changes to the materials contained on its website at any time without notice. However Construct Software, Inc. does not make any commitment to update the materials.

48 | 49 |

6. Links

50 | 51 |

Construct Software, Inc. has not reviewed all of the sites linked to its website and is not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by Construct Software, Inc. of the site. Use of any such linked website is at the user's own risk.

52 | 53 |

7. Modifications

54 | 55 |

Construct Software, Inc. may revise these terms of service for its website at any time without notice. By using this website you are agreeing to be bound by the then current version of these terms of service.

56 | 57 |

8. Governing Law

58 | 59 |

These terms and conditions are governed by and construed in accordance with the laws of California and you irrevocably submit to the exclusive jurisdiction of the courts in that State or location.

60 | 61 |

Privacy Policy

62 | 63 |

Your privacy is important to us.

64 | 65 |

It is Construct Software, Inc.'s policy to respect your privacy regarding any information we may collect while operating our website. Accordingly, we have developed this privacy policy in order for you to understand how we collect, use, communicate, disclose and otherwise make use of personal information. We have outlined our privacy policy below.

66 | 67 |
    68 |
  • We will collect personal information by lawful and fair means and, where appropriate, with the knowledge or consent of the individual concerned.
  • 69 |
  • Before or at the time of collecting personal information, we will identify the purposes for which information is being collected.
  • 70 |
  • We will collect and use personal information solely for fulfilling those purposes specified by us and for other ancillary purposes, unless we obtain the consent of the individual concerned or as required by law.
  • 71 |
  • Personal data should be relevant to the purposes for which it is to be used, and, to the extent necessary for those purposes, should be accurate, complete, and up-to-date.
  • 72 |
  • We will protect personal information by using reasonable security safeguards against loss or theft, as well as unauthorized access, disclosure, copying, use or modification.
  • 73 |
  • We will make readily available to customers information about our policies and practices relating to the management of personal information.
  • 74 |
  • We will only retain personal information for as long as necessary for the fulfilment of those purposes.
  • 75 |
76 | 77 |

We are committed to conducting our business in accordance with these principles in order to ensure that the confidentiality of personal information is protected and maintained. Construct Software, Inc. may change this privacy policy from time to time at Construct Software, Inc.'s sole discretion.

78 | 79 |
80 |
81 |
82 |
83 | 84 | 85 | {% endblock %} 86 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | accord@^0.26.3: 6 | version "0.26.4" 7 | resolved "https://registry.yarnpkg.com/accord/-/accord-0.26.4.tgz#fc4c8d3ebab406a07cb28819b859651c44a92e80" 8 | dependencies: 9 | convert-source-map "^1.2.0" 10 | glob "^7.0.5" 11 | indx "^0.2.3" 12 | lodash.clone "^4.3.2" 13 | lodash.defaults "^4.0.1" 14 | lodash.flatten "^4.2.0" 15 | lodash.merge "^4.4.0" 16 | lodash.partialright "^4.1.4" 17 | lodash.pick "^4.2.1" 18 | lodash.uniq "^4.3.0" 19 | resolve "^1.1.7" 20 | semver "^5.3.0" 21 | uglify-js "^2.7.0" 22 | when "^3.7.7" 23 | 24 | acorn@4.X: 25 | version "4.0.11" 26 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" 27 | 28 | align-text@^0.1.1, align-text@^0.1.3: 29 | version "0.1.4" 30 | resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" 31 | dependencies: 32 | kind-of "^3.0.2" 33 | longest "^1.0.1" 34 | repeat-string "^1.5.2" 35 | 36 | amdefine@>=0.0.4: 37 | version "1.0.1" 38 | resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 39 | 40 | ansi-regex@^0.2.0, ansi-regex@^0.2.1: 41 | version "0.2.1" 42 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" 43 | 44 | ansi-regex@^2.0.0: 45 | version "2.1.1" 46 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 47 | 48 | ansi-styles@^1.1.0: 49 | version "1.1.0" 50 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" 51 | 52 | ansi-styles@^2.2.1: 53 | version "2.2.1" 54 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 55 | 56 | archy@^1.0.0: 57 | version "1.0.0" 58 | resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" 59 | 60 | arr-diff@^2.0.0: 61 | version "2.0.0" 62 | resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" 63 | dependencies: 64 | arr-flatten "^1.0.1" 65 | 66 | arr-flatten@^1.0.1: 67 | version "1.0.1" 68 | resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" 69 | 70 | array-differ@^1.0.0: 71 | version "1.0.0" 72 | resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" 73 | 74 | array-uniq@^1.0.2: 75 | version "1.0.3" 76 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 77 | 78 | array-unique@^0.2.1: 79 | version "0.2.1" 80 | resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" 81 | 82 | arrify@^1.0.1: 83 | version "1.0.1" 84 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 85 | 86 | asap@~2.0.3: 87 | version "2.0.5" 88 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" 89 | 90 | asn1@~0.2.3: 91 | version "0.2.3" 92 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" 93 | 94 | assert-plus@^0.2.0: 95 | version "0.2.0" 96 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" 97 | 98 | assert-plus@^1.0.0: 99 | version "1.0.0" 100 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" 101 | 102 | async@^1.5.0: 103 | version "1.5.2" 104 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 105 | 106 | async@~0.2.6: 107 | version "0.2.10" 108 | resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" 109 | 110 | asynckit@^0.4.0: 111 | version "0.4.0" 112 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 113 | 114 | atob@~1.1.0: 115 | version "1.1.3" 116 | resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" 117 | 118 | aws-sign2@~0.6.0: 119 | version "0.6.0" 120 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" 121 | 122 | aws4@^1.2.1: 123 | version "1.6.0" 124 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" 125 | 126 | balanced-match@^0.4.1: 127 | version "0.4.2" 128 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" 129 | 130 | bcrypt-pbkdf@^1.0.0: 131 | version "1.0.1" 132 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" 133 | dependencies: 134 | tweetnacl "^0.14.3" 135 | 136 | beeper@^1.0.0: 137 | version "1.1.1" 138 | resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" 139 | 140 | bignumber.js@^2.1.0: 141 | version "2.4.0" 142 | resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-2.4.0.tgz#838a992da9f9d737e0f4b2db0be62bb09dd0c5e8" 143 | 144 | bmp-js@0.0.1: 145 | version "0.0.1" 146 | resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.1.tgz#5ad0147099d13a9f38aa7b99af1d6e78666ed37f" 147 | 148 | body-parser@~1.14.0: 149 | version "1.14.2" 150 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.14.2.tgz#1015cb1fe2c443858259581db53332f8d0cf50f9" 151 | dependencies: 152 | bytes "2.2.0" 153 | content-type "~1.0.1" 154 | debug "~2.2.0" 155 | depd "~1.1.0" 156 | http-errors "~1.3.1" 157 | iconv-lite "0.4.13" 158 | on-finished "~2.3.0" 159 | qs "5.2.0" 160 | raw-body "~2.1.5" 161 | type-is "~1.6.10" 162 | 163 | boolbase@~1.0.0: 164 | version "1.0.0" 165 | resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 166 | 167 | boom@2.x.x: 168 | version "2.10.1" 169 | resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" 170 | dependencies: 171 | hoek "2.x.x" 172 | 173 | brace-expansion@^1.0.0: 174 | version "1.1.6" 175 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" 176 | dependencies: 177 | balanced-match "^0.4.1" 178 | concat-map "0.0.1" 179 | 180 | braces@^1.8.2: 181 | version "1.8.5" 182 | resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" 183 | dependencies: 184 | expand-range "^1.8.1" 185 | preserve "^0.2.0" 186 | repeat-element "^1.1.2" 187 | 188 | buffer-equal@0.0.1: 189 | version "0.0.1" 190 | resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" 191 | 192 | buffer-shims@^1.0.0: 193 | version "1.0.0" 194 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 195 | 196 | bytes@2.2.0: 197 | version "2.2.0" 198 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588" 199 | 200 | bytes@2.4.0: 201 | version "2.4.0" 202 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" 203 | 204 | camelcase@^1.0.2: 205 | version "1.2.1" 206 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" 207 | 208 | camelcase@^2.0.1: 209 | version "2.1.1" 210 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" 211 | 212 | caseless@~0.11.0: 213 | version "0.11.0" 214 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" 215 | 216 | center-align@^0.1.1: 217 | version "0.1.3" 218 | resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" 219 | dependencies: 220 | align-text "^0.1.3" 221 | lazy-cache "^1.0.3" 222 | 223 | chalk@^0.5.1: 224 | version "0.5.1" 225 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" 226 | dependencies: 227 | ansi-styles "^1.1.0" 228 | escape-string-regexp "^1.0.0" 229 | has-ansi "^0.1.0" 230 | strip-ansi "^0.3.0" 231 | supports-color "^0.2.0" 232 | 233 | chalk@^1.0.0, chalk@^1.1.1: 234 | version "1.1.3" 235 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 236 | dependencies: 237 | ansi-styles "^2.2.1" 238 | escape-string-regexp "^1.0.2" 239 | has-ansi "^2.0.0" 240 | strip-ansi "^3.0.0" 241 | supports-color "^2.0.0" 242 | 243 | cheerio@^0.19.0: 244 | version "0.19.0" 245 | resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" 246 | dependencies: 247 | css-select "~1.0.0" 248 | dom-serializer "~0.1.0" 249 | entities "~1.1.1" 250 | htmlparser2 "~3.8.1" 251 | lodash "^3.2.0" 252 | 253 | cliui@^2.1.0: 254 | version "2.1.0" 255 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" 256 | dependencies: 257 | center-align "^0.1.1" 258 | right-align "^0.1.1" 259 | wordwrap "0.0.2" 260 | 261 | cliui@^3.0.3: 262 | version "3.2.0" 263 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" 264 | dependencies: 265 | string-width "^1.0.1" 266 | strip-ansi "^3.0.1" 267 | wrap-ansi "^2.0.0" 268 | 269 | clone-stats@^0.0.1: 270 | version "0.0.1" 271 | resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" 272 | 273 | clone@^0.2.0: 274 | version "0.2.0" 275 | resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" 276 | 277 | clone@^1.0.0, clone@^1.0.2: 278 | version "1.0.2" 279 | resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" 280 | 281 | code-point-at@^1.0.0: 282 | version "1.1.0" 283 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 284 | 285 | colors@^1.1.2: 286 | version "1.1.2" 287 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" 288 | 289 | combined-stream@^1.0.5, combined-stream@~1.0.5: 290 | version "1.0.5" 291 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" 292 | dependencies: 293 | delayed-stream "~1.0.0" 294 | 295 | commander@^2.9.0: 296 | version "2.9.0" 297 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" 298 | dependencies: 299 | graceful-readlink ">= 1.0.0" 300 | 301 | concat-map@0.0.1: 302 | version "0.0.1" 303 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 304 | 305 | concat-stream@1.5.0: 306 | version "1.5.0" 307 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" 308 | dependencies: 309 | inherits "~2.0.1" 310 | readable-stream "~2.0.0" 311 | typedarray "~0.0.5" 312 | 313 | content-type@~1.0.1: 314 | version "1.0.2" 315 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" 316 | 317 | convert-source-map@1.X, convert-source-map@^1.2.0: 318 | version "1.4.0" 319 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" 320 | 321 | core-util-is@~1.0.0: 322 | version "1.0.2" 323 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 324 | 325 | cryptiles@2.x.x: 326 | version "2.0.5" 327 | resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" 328 | dependencies: 329 | boom "2.x.x" 330 | 331 | css-select@~1.0.0: 332 | version "1.0.0" 333 | resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" 334 | dependencies: 335 | boolbase "~1.0.0" 336 | css-what "1.0" 337 | domutils "1.4" 338 | nth-check "~1.0.0" 339 | 340 | css-what@1.0: 341 | version "1.0.0" 342 | resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" 343 | 344 | css@2.X: 345 | version "2.2.1" 346 | resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" 347 | dependencies: 348 | inherits "^2.0.1" 349 | source-map "^0.1.38" 350 | source-map-resolve "^0.3.0" 351 | urix "^0.1.0" 352 | 353 | dashdash@^1.12.0: 354 | version "1.14.1" 355 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" 356 | dependencies: 357 | assert-plus "^1.0.0" 358 | 359 | dateformat@^2.0.0: 360 | version "2.0.0" 361 | resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.0.0.tgz#2743e3abb5c3fc2462e527dca445e04e9f4dee17" 362 | 363 | debug-fabulous@0.0.X: 364 | version "0.0.4" 365 | resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" 366 | dependencies: 367 | debug "2.X" 368 | lazy-debug-legacy "0.0.X" 369 | object-assign "4.1.0" 370 | 371 | debug@0.7.4: 372 | version "0.7.4" 373 | resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" 374 | 375 | debug@2.X, debug@^2.1.0, debug@^2.2.0: 376 | version "2.6.1" 377 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" 378 | dependencies: 379 | ms "0.7.2" 380 | 381 | debug@~2.2.0: 382 | version "2.2.0" 383 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" 384 | dependencies: 385 | ms "0.7.1" 386 | 387 | decamelize@^1.0.0, decamelize@^1.1.1: 388 | version "1.2.0" 389 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 390 | 391 | defaults@^1.0.0: 392 | version "1.0.3" 393 | resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" 394 | dependencies: 395 | clone "^1.0.2" 396 | 397 | delayed-stream@~1.0.0: 398 | version "1.0.0" 399 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 400 | 401 | depd@~1.1.0: 402 | version "1.1.0" 403 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" 404 | 405 | deprecated@^0.0.1: 406 | version "0.0.1" 407 | resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" 408 | 409 | detect-file@^0.1.0: 410 | version "0.1.0" 411 | resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" 412 | dependencies: 413 | fs-exists-sync "^0.1.0" 414 | 415 | detect-newline@2.X: 416 | version "2.1.0" 417 | resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" 418 | 419 | dom-serializer@0, dom-serializer@~0.1.0: 420 | version "0.1.0" 421 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" 422 | dependencies: 423 | domelementtype "~1.1.1" 424 | entities "~1.1.1" 425 | 426 | dom-walk@^0.1.0: 427 | version "0.1.1" 428 | resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" 429 | 430 | domelementtype@1, domelementtype@~1.1.1: 431 | version "1.1.3" 432 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" 433 | 434 | domhandler@2.3: 435 | version "2.3.0" 436 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" 437 | dependencies: 438 | domelementtype "1" 439 | 440 | domutils@1.4: 441 | version "1.4.3" 442 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" 443 | dependencies: 444 | domelementtype "1" 445 | 446 | domutils@1.5: 447 | version "1.5.1" 448 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" 449 | dependencies: 450 | dom-serializer "0" 451 | domelementtype "1" 452 | 453 | duplexer2@0.0.2: 454 | version "0.0.2" 455 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" 456 | dependencies: 457 | readable-stream "~1.1.9" 458 | 459 | duplexer@~0.1.1: 460 | version "0.1.1" 461 | resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" 462 | 463 | ecc-jsbn@~0.1.1: 464 | version "0.1.1" 465 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" 466 | dependencies: 467 | jsbn "~0.1.0" 468 | 469 | ee-first@1.1.1: 470 | version "1.1.1" 471 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 472 | 473 | end-of-stream@~0.1.5: 474 | version "0.1.5" 475 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" 476 | dependencies: 477 | once "~1.3.0" 478 | 479 | entities@1.0: 480 | version "1.0.0" 481 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" 482 | 483 | entities@~1.1.1: 484 | version "1.1.1" 485 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" 486 | 487 | errno@^0.1.1: 488 | version "0.1.4" 489 | resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" 490 | dependencies: 491 | prr "~0.0.0" 492 | 493 | es6-promise@^3.0.2: 494 | version "3.3.1" 495 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" 496 | 497 | es6-promise@~4.0.3: 498 | version "4.0.5" 499 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" 500 | 501 | escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2: 502 | version "1.0.5" 503 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 504 | 505 | event-stream@^3.1.7: 506 | version "3.3.4" 507 | resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" 508 | dependencies: 509 | duplexer "~0.1.1" 510 | from "~0" 511 | map-stream "~0.1.0" 512 | pause-stream "0.0.11" 513 | split "0.3" 514 | stream-combiner "~0.0.4" 515 | through "~2.3.1" 516 | 517 | exif-parser@^0.1.9: 518 | version "0.1.9" 519 | resolved "https://registry.yarnpkg.com/exif-parser/-/exif-parser-0.1.9.tgz#1d087e05fd2b079e3a8eaf8ff249978cb5f6fba7" 520 | 521 | expand-brackets@^0.1.4: 522 | version "0.1.5" 523 | resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" 524 | dependencies: 525 | is-posix-bracket "^0.1.0" 526 | 527 | expand-range@^1.8.1: 528 | version "1.8.2" 529 | resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" 530 | dependencies: 531 | fill-range "^2.1.0" 532 | 533 | expand-tilde@^1.2.1, expand-tilde@^1.2.2: 534 | version "1.2.2" 535 | resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" 536 | dependencies: 537 | os-homedir "^1.0.1" 538 | 539 | extend@^3.0.0, extend@~3.0.0: 540 | version "3.0.0" 541 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" 542 | 543 | extglob@^0.3.1: 544 | version "0.3.2" 545 | resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" 546 | dependencies: 547 | is-extglob "^1.0.0" 548 | 549 | extract-zip@~1.5.0: 550 | version "1.5.0" 551 | resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4" 552 | dependencies: 553 | concat-stream "1.5.0" 554 | debug "0.7.4" 555 | mkdirp "0.5.0" 556 | yauzl "2.4.1" 557 | 558 | extsprintf@1.0.2: 559 | version "1.0.2" 560 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" 561 | 562 | fancy-log@^1.1.0: 563 | version "1.3.0" 564 | resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" 565 | dependencies: 566 | chalk "^1.1.1" 567 | time-stamp "^1.0.0" 568 | 569 | favicons@^4.6.1: 570 | version "4.8.3" 571 | resolved "https://registry.yarnpkg.com/favicons/-/favicons-4.8.3.tgz#c7a2ccd9a5667baf2c01bbe5c78f7564457a7693" 572 | dependencies: 573 | async "^1.5.0" 574 | cheerio "^0.19.0" 575 | clone "^1.0.2" 576 | colors "^1.1.2" 577 | harmony-reflect "^1.4.2" 578 | image-size "^0.4.0" 579 | jimp "^0.2.13" 580 | jsontoxml "0.0.11" 581 | merge-defaults "^0.2.1" 582 | mkdirp "^0.5.1" 583 | node-rest-client "^1.5.1" 584 | require-directory "^2.1.1" 585 | svg2png "~3.0.1" 586 | through2 "^2.0.0" 587 | tinycolor2 "^1.1.2" 588 | to-ico "^1.1.2" 589 | underscore "^1.8.3" 590 | vinyl "^1.1.0" 591 | 592 | faye-websocket@~0.7.2: 593 | version "0.7.3" 594 | resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.7.3.tgz#cc4074c7f4a4dfd03af54dd65c354b135132ce11" 595 | dependencies: 596 | websocket-driver ">=0.3.6" 597 | 598 | fd-slicer@~1.0.1: 599 | version "1.0.1" 600 | resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" 601 | dependencies: 602 | pend "~1.2.0" 603 | 604 | file-type@^3.1.0, file-type@^3.8.0: 605 | version "3.9.0" 606 | resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" 607 | 608 | filename-regex@^2.0.0: 609 | version "2.0.0" 610 | resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" 611 | 612 | fill-range@^2.1.0: 613 | version "2.2.3" 614 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" 615 | dependencies: 616 | is-number "^2.1.0" 617 | isobject "^2.0.0" 618 | randomatic "^1.1.3" 619 | repeat-element "^1.1.2" 620 | repeat-string "^1.5.2" 621 | 622 | find-index@^0.1.1: 623 | version "0.1.1" 624 | resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" 625 | 626 | findup-sync@^0.4.2: 627 | version "0.4.3" 628 | resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" 629 | dependencies: 630 | detect-file "^0.1.0" 631 | is-glob "^2.0.1" 632 | micromatch "^2.3.7" 633 | resolve-dir "^0.1.0" 634 | 635 | fined@^1.0.1: 636 | version "1.0.2" 637 | resolved "https://registry.yarnpkg.com/fined/-/fined-1.0.2.tgz#5b28424b760d7598960b7ef8480dff8ad3660e97" 638 | dependencies: 639 | expand-tilde "^1.2.1" 640 | lodash.assignwith "^4.0.7" 641 | lodash.isempty "^4.2.1" 642 | lodash.isplainobject "^4.0.4" 643 | lodash.isstring "^4.0.1" 644 | lodash.pick "^4.2.1" 645 | parse-filepath "^1.0.1" 646 | 647 | first-chunk-stream@^1.0.0: 648 | version "1.0.0" 649 | resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" 650 | 651 | flagged-respawn@^0.3.2: 652 | version "0.3.2" 653 | resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" 654 | 655 | for-each@^0.3.2: 656 | version "0.3.2" 657 | resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" 658 | dependencies: 659 | is-function "~1.0.0" 660 | 661 | for-in@^0.1.5: 662 | version "0.1.6" 663 | resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" 664 | 665 | for-own@^0.1.4: 666 | version "0.1.4" 667 | resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" 668 | dependencies: 669 | for-in "^0.1.5" 670 | 671 | forever-agent@~0.6.1: 672 | version "0.6.1" 673 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" 674 | 675 | form-data@~2.1.1: 676 | version "2.1.2" 677 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" 678 | dependencies: 679 | asynckit "^0.4.0" 680 | combined-stream "^1.0.5" 681 | mime-types "^2.1.12" 682 | 683 | from@~0: 684 | version "0.1.3" 685 | resolved "https://registry.yarnpkg.com/from/-/from-0.1.3.tgz#ef63ac2062ac32acf7862e0d40b44b896f22f3bc" 686 | 687 | fs-exists-sync@^0.1.0: 688 | version "0.1.0" 689 | resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" 690 | 691 | fs-extra@~1.0.0: 692 | version "1.0.0" 693 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" 694 | dependencies: 695 | graceful-fs "^4.1.2" 696 | jsonfile "^2.1.0" 697 | klaw "^1.0.0" 698 | 699 | fs.realpath@^1.0.0: 700 | version "1.0.0" 701 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 702 | 703 | gaze@^0.5.1: 704 | version "0.5.2" 705 | resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" 706 | dependencies: 707 | globule "~0.1.0" 708 | 709 | generate-function@^2.0.0: 710 | version "2.0.0" 711 | resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" 712 | 713 | generate-object-property@^1.1.0: 714 | version "1.2.0" 715 | resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" 716 | dependencies: 717 | is-property "^1.0.0" 718 | 719 | get-stream@^2.0.0: 720 | version "2.3.1" 721 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" 722 | dependencies: 723 | object-assign "^4.0.1" 724 | pinkie-promise "^2.0.0" 725 | 726 | getpass@^0.1.1: 727 | version "0.1.6" 728 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" 729 | dependencies: 730 | assert-plus "^1.0.0" 731 | 732 | glob-base@^0.3.0: 733 | version "0.3.0" 734 | resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 735 | dependencies: 736 | glob-parent "^2.0.0" 737 | is-glob "^2.0.0" 738 | 739 | glob-parent@^2.0.0: 740 | version "2.0.0" 741 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" 742 | dependencies: 743 | is-glob "^2.0.0" 744 | 745 | glob-stream@^3.1.5: 746 | version "3.1.18" 747 | resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" 748 | dependencies: 749 | glob "^4.3.1" 750 | glob2base "^0.0.12" 751 | minimatch "^2.0.1" 752 | ordered-read-streams "^0.1.0" 753 | through2 "^0.6.1" 754 | unique-stream "^1.0.0" 755 | 756 | glob-watcher@^0.0.6: 757 | version "0.0.6" 758 | resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" 759 | dependencies: 760 | gaze "^0.5.1" 761 | 762 | glob2base@^0.0.12: 763 | version "0.0.12" 764 | resolved "http://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" 765 | dependencies: 766 | find-index "^0.1.1" 767 | 768 | glob@^4.3.1: 769 | version "4.5.3" 770 | resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" 771 | dependencies: 772 | inflight "^1.0.4" 773 | inherits "2" 774 | minimatch "^2.0.1" 775 | once "^1.3.0" 776 | 777 | glob@^7.0.5: 778 | version "7.1.1" 779 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" 780 | dependencies: 781 | fs.realpath "^1.0.0" 782 | inflight "^1.0.4" 783 | inherits "2" 784 | minimatch "^3.0.2" 785 | once "^1.3.0" 786 | path-is-absolute "^1.0.0" 787 | 788 | glob@~3.1.21: 789 | version "3.1.21" 790 | resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" 791 | dependencies: 792 | graceful-fs "~1.2.0" 793 | inherits "1" 794 | minimatch "~0.2.11" 795 | 796 | global-modules@^0.2.3: 797 | version "0.2.3" 798 | resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" 799 | dependencies: 800 | global-prefix "^0.1.4" 801 | is-windows "^0.2.0" 802 | 803 | global-prefix@^0.1.4: 804 | version "0.1.5" 805 | resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" 806 | dependencies: 807 | homedir-polyfill "^1.0.0" 808 | ini "^1.3.4" 809 | is-windows "^0.2.0" 810 | which "^1.2.12" 811 | 812 | global@~4.3.0: 813 | version "4.3.1" 814 | resolved "https://registry.yarnpkg.com/global/-/global-4.3.1.tgz#5f757908c7cbabce54f386ae440e11e26b7916df" 815 | dependencies: 816 | min-document "^2.19.0" 817 | process "~0.5.1" 818 | 819 | globule@~0.1.0: 820 | version "0.1.0" 821 | resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" 822 | dependencies: 823 | glob "~3.1.21" 824 | lodash "~1.0.1" 825 | minimatch "~0.2.11" 826 | 827 | glogg@^1.0.0: 828 | version "1.0.0" 829 | resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" 830 | dependencies: 831 | sparkles "^1.0.0" 832 | 833 | graceful-fs@4.X, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: 834 | version "4.1.11" 835 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 836 | 837 | graceful-fs@^3.0.0: 838 | version "3.0.11" 839 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" 840 | dependencies: 841 | natives "^1.1.0" 842 | 843 | graceful-fs@~1.2.0: 844 | version "1.2.3" 845 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" 846 | 847 | "graceful-readlink@>= 1.0.0": 848 | version "1.0.1" 849 | resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" 850 | 851 | gulp-favicons@^2.2.7: 852 | version "2.2.7" 853 | resolved "https://registry.yarnpkg.com/gulp-favicons/-/gulp-favicons-2.2.7.tgz#2286e02e05cee3c94c27d5abd6060cd93aa786e5" 854 | dependencies: 855 | favicons "^4.6.1" 856 | 857 | gulp-less@^3.3.0: 858 | version "3.3.0" 859 | resolved "https://registry.yarnpkg.com/gulp-less/-/gulp-less-3.3.0.tgz#d085565da3c810307fde7c7874e86520dc503234" 860 | dependencies: 861 | accord "^0.26.3" 862 | gulp-util "^3.0.7" 863 | less "2.6.x || ^2.7.1" 864 | object-assign "^4.0.1" 865 | through2 "^2.0.0" 866 | vinyl-sourcemaps-apply "^0.2.0" 867 | 868 | gulp-livereload@^3.8.1: 869 | version "3.8.1" 870 | resolved "https://registry.yarnpkg.com/gulp-livereload/-/gulp-livereload-3.8.1.tgz#00f744b2d749d3e9e3746589c8a44acac779b50f" 871 | dependencies: 872 | chalk "^0.5.1" 873 | debug "^2.1.0" 874 | event-stream "^3.1.7" 875 | gulp-util "^3.0.2" 876 | lodash.assign "^3.0.0" 877 | mini-lr "^0.1.8" 878 | 879 | gulp-sourcemaps@^2.4.1: 880 | version "2.4.1" 881 | resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.4.1.tgz#8f65dc5c0d07b2fd5c88bc60ec7f13e56716bf74" 882 | dependencies: 883 | acorn "4.X" 884 | convert-source-map "1.X" 885 | css "2.X" 886 | debug-fabulous "0.0.X" 887 | detect-newline "2.X" 888 | graceful-fs "4.X" 889 | source-map "0.X" 890 | strip-bom "3.X" 891 | through2 "2.X" 892 | vinyl "1.X" 893 | 894 | gulp-util@^3.0.0, gulp-util@^3.0.2, gulp-util@^3.0.7: 895 | version "3.0.8" 896 | resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" 897 | dependencies: 898 | array-differ "^1.0.0" 899 | array-uniq "^1.0.2" 900 | beeper "^1.0.0" 901 | chalk "^1.0.0" 902 | dateformat "^2.0.0" 903 | fancy-log "^1.1.0" 904 | gulplog "^1.0.0" 905 | has-gulplog "^0.1.0" 906 | lodash._reescape "^3.0.0" 907 | lodash._reevaluate "^3.0.0" 908 | lodash._reinterpolate "^3.0.0" 909 | lodash.template "^3.0.0" 910 | minimist "^1.1.0" 911 | multipipe "^0.1.2" 912 | object-assign "^3.0.0" 913 | replace-ext "0.0.1" 914 | through2 "^2.0.0" 915 | vinyl "^0.5.0" 916 | 917 | gulp@^3.9.1: 918 | version "3.9.1" 919 | resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" 920 | dependencies: 921 | archy "^1.0.0" 922 | chalk "^1.0.0" 923 | deprecated "^0.0.1" 924 | gulp-util "^3.0.0" 925 | interpret "^1.0.0" 926 | liftoff "^2.1.0" 927 | minimist "^1.1.0" 928 | orchestrator "^0.3.0" 929 | pretty-hrtime "^1.0.0" 930 | semver "^4.1.0" 931 | tildify "^1.0.0" 932 | v8flags "^2.0.2" 933 | vinyl-fs "^0.3.0" 934 | 935 | gulplog@^1.0.0: 936 | version "1.0.0" 937 | resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" 938 | dependencies: 939 | glogg "^1.0.0" 940 | 941 | har-validator@~2.0.6: 942 | version "2.0.6" 943 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" 944 | dependencies: 945 | chalk "^1.1.1" 946 | commander "^2.9.0" 947 | is-my-json-valid "^2.12.4" 948 | pinkie-promise "^2.0.0" 949 | 950 | harmony-reflect@^1.4.2: 951 | version "1.5.1" 952 | resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.5.1.tgz#b54ca617b00cc8aef559bbb17b3d85431dc7e329" 953 | 954 | has-ansi@^0.1.0: 955 | version "0.1.0" 956 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" 957 | dependencies: 958 | ansi-regex "^0.2.0" 959 | 960 | has-ansi@^2.0.0: 961 | version "2.0.0" 962 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 963 | dependencies: 964 | ansi-regex "^2.0.0" 965 | 966 | has-gulplog@^0.1.0: 967 | version "0.1.0" 968 | resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" 969 | dependencies: 970 | sparkles "^1.0.0" 971 | 972 | hasha@~2.2.0: 973 | version "2.2.0" 974 | resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" 975 | dependencies: 976 | is-stream "^1.0.1" 977 | pinkie-promise "^2.0.0" 978 | 979 | hawk@~3.1.3: 980 | version "3.1.3" 981 | resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" 982 | dependencies: 983 | boom "2.x.x" 984 | cryptiles "2.x.x" 985 | hoek "2.x.x" 986 | sntp "1.x.x" 987 | 988 | hoek@2.x.x: 989 | version "2.16.3" 990 | resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" 991 | 992 | homedir-polyfill@^1.0.0: 993 | version "1.0.1" 994 | resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" 995 | dependencies: 996 | parse-passwd "^1.0.0" 997 | 998 | htmlparser2@~3.8.1: 999 | version "3.8.3" 1000 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" 1001 | dependencies: 1002 | domelementtype "1" 1003 | domhandler "2.3" 1004 | domutils "1.5" 1005 | entities "1.0" 1006 | readable-stream "1.1" 1007 | 1008 | http-errors@~1.3.1: 1009 | version "1.3.1" 1010 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" 1011 | dependencies: 1012 | inherits "~2.0.1" 1013 | statuses "1" 1014 | 1015 | http-signature@~1.1.0: 1016 | version "1.1.1" 1017 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" 1018 | dependencies: 1019 | assert-plus "^0.2.0" 1020 | jsprim "^1.2.2" 1021 | sshpk "^1.7.0" 1022 | 1023 | icojs@^0.4.1: 1024 | version "0.4.1" 1025 | resolved "https://registry.yarnpkg.com/icojs/-/icojs-0.4.1.tgz#ac8c1870c223bb2ed33886ba2d34f81f00dff4d3" 1026 | dependencies: 1027 | jimp "^0.2.24" 1028 | 1029 | iconv-lite@0.4.13: 1030 | version "0.4.13" 1031 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" 1032 | 1033 | image-size@^0.4.0: 1034 | version "0.4.0" 1035 | resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.4.0.tgz#d4b4e1f61952e4cbc1cea9a6b0c915fecb707510" 1036 | 1037 | image-size@^0.5.0, image-size@~0.5.0: 1038 | version "0.5.1" 1039 | resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.1.tgz#28eea8548a4b1443480ddddc1e083ae54652439f" 1040 | 1041 | indx@^0.2.3: 1042 | version "0.2.3" 1043 | resolved "https://registry.yarnpkg.com/indx/-/indx-0.2.3.tgz#15dcf56ee9cf65c0234c513c27fbd580e70fbc50" 1044 | 1045 | inflight@^1.0.4: 1046 | version "1.0.6" 1047 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 1048 | dependencies: 1049 | once "^1.3.0" 1050 | wrappy "1" 1051 | 1052 | inherits@1: 1053 | version "1.0.2" 1054 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" 1055 | 1056 | inherits@2, inherits@^2.0.1, inherits@~2.0.1: 1057 | version "2.0.3" 1058 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 1059 | 1060 | ini@^1.3.4: 1061 | version "1.3.4" 1062 | resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" 1063 | 1064 | interpret@^1.0.0: 1065 | version "1.0.1" 1066 | resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" 1067 | 1068 | invert-kv@^1.0.0: 1069 | version "1.0.0" 1070 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" 1071 | 1072 | ip-regex@^1.0.1: 1073 | version "1.0.3" 1074 | resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" 1075 | 1076 | is-absolute@^0.2.3: 1077 | version "0.2.6" 1078 | resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" 1079 | dependencies: 1080 | is-relative "^0.2.1" 1081 | is-windows "^0.2.0" 1082 | 1083 | is-buffer@^1.0.2: 1084 | version "1.1.4" 1085 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" 1086 | 1087 | is-dotfile@^1.0.0: 1088 | version "1.0.2" 1089 | resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" 1090 | 1091 | is-equal-shallow@^0.1.3: 1092 | version "0.1.3" 1093 | resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" 1094 | dependencies: 1095 | is-primitive "^2.0.0" 1096 | 1097 | is-extendable@^0.1.1: 1098 | version "0.1.1" 1099 | resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" 1100 | 1101 | is-extglob@^1.0.0: 1102 | version "1.0.0" 1103 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" 1104 | 1105 | is-fullwidth-code-point@^1.0.0: 1106 | version "1.0.0" 1107 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 1108 | dependencies: 1109 | number-is-nan "^1.0.0" 1110 | 1111 | is-function@^1.0.1, is-function@~1.0.0: 1112 | version "1.0.1" 1113 | resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" 1114 | 1115 | is-glob@^2.0.0, is-glob@^2.0.1: 1116 | version "2.0.1" 1117 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" 1118 | dependencies: 1119 | is-extglob "^1.0.0" 1120 | 1121 | is-my-json-valid@^2.12.4: 1122 | version "2.15.0" 1123 | resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" 1124 | dependencies: 1125 | generate-function "^2.0.0" 1126 | generate-object-property "^1.1.0" 1127 | jsonpointer "^4.0.0" 1128 | xtend "^4.0.0" 1129 | 1130 | is-number@^2.0.2, is-number@^2.1.0: 1131 | version "2.1.0" 1132 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" 1133 | dependencies: 1134 | kind-of "^3.0.2" 1135 | 1136 | is-posix-bracket@^0.1.0: 1137 | version "0.1.1" 1138 | resolved "http://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" 1139 | 1140 | is-primitive@^2.0.0: 1141 | version "2.0.0" 1142 | resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" 1143 | 1144 | is-property@^1.0.0: 1145 | version "1.0.2" 1146 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" 1147 | 1148 | is-relative@^0.2.1: 1149 | version "0.2.1" 1150 | resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" 1151 | dependencies: 1152 | is-unc-path "^0.1.1" 1153 | 1154 | is-stream@^1.0.1: 1155 | version "1.1.0" 1156 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 1157 | 1158 | is-typedarray@~1.0.0: 1159 | version "1.0.0" 1160 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 1161 | 1162 | is-unc-path@^0.1.1: 1163 | version "0.1.2" 1164 | resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" 1165 | dependencies: 1166 | unc-path-regex "^0.1.0" 1167 | 1168 | is-utf8@^0.2.0: 1169 | version "0.2.1" 1170 | resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" 1171 | 1172 | is-windows@^0.2.0: 1173 | version "0.2.0" 1174 | resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" 1175 | 1176 | isarray@0.0.1: 1177 | version "0.0.1" 1178 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 1179 | 1180 | isarray@1.0.0, isarray@~1.0.0: 1181 | version "1.0.0" 1182 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 1183 | 1184 | isexe@^1.1.1: 1185 | version "1.1.2" 1186 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" 1187 | 1188 | isobject@^2.0.0: 1189 | version "2.1.0" 1190 | resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" 1191 | dependencies: 1192 | isarray "1.0.0" 1193 | 1194 | isstream@~0.1.2: 1195 | version "0.1.2" 1196 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 1197 | 1198 | jimp@^0.2.13, jimp@^0.2.21, jimp@^0.2.24: 1199 | version "0.2.27" 1200 | resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.2.27.tgz#41ef5082d8b63201d54747e04fe8bcacbaf25474" 1201 | dependencies: 1202 | bignumber.js "^2.1.0" 1203 | bmp-js "0.0.1" 1204 | es6-promise "^3.0.2" 1205 | exif-parser "^0.1.9" 1206 | file-type "^3.1.0" 1207 | jpeg-js "^0.2.0" 1208 | load-bmfont "^1.2.3" 1209 | mime "^1.3.4" 1210 | pixelmatch "^4.0.0" 1211 | pngjs "^3.0.0" 1212 | read-chunk "^1.0.1" 1213 | request "^2.65.0" 1214 | stream-to-buffer "^0.1.0" 1215 | tinycolor2 "^1.1.2" 1216 | url-regex "^3.0.0" 1217 | 1218 | jodid25519@^1.0.0: 1219 | version "1.0.2" 1220 | resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" 1221 | dependencies: 1222 | jsbn "~0.1.0" 1223 | 1224 | jpeg-js@^0.1.1: 1225 | version "0.1.2" 1226 | resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.1.2.tgz#135b992c0575c985cfa0f494a3227ed238583ece" 1227 | 1228 | jpeg-js@^0.2.0: 1229 | version "0.2.0" 1230 | resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.2.0.tgz#53e448ec9d263e683266467e9442d2c5a2ef5482" 1231 | 1232 | jsbn@~0.1.0: 1233 | version "0.1.1" 1234 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" 1235 | 1236 | json-schema@0.2.3: 1237 | version "0.2.3" 1238 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" 1239 | 1240 | json-stringify-safe@~5.0.1: 1241 | version "5.0.1" 1242 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 1243 | 1244 | jsonfile@^2.1.0: 1245 | version "2.4.0" 1246 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" 1247 | optionalDependencies: 1248 | graceful-fs "^4.1.6" 1249 | 1250 | jsonpointer@^4.0.0: 1251 | version "4.0.1" 1252 | resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" 1253 | 1254 | jsontoxml@0.0.11: 1255 | version "0.0.11" 1256 | resolved "https://registry.yarnpkg.com/jsontoxml/-/jsontoxml-0.0.11.tgz#373ab5b2070be3737a5fb3e32fd1b7b81870caa4" 1257 | 1258 | jsprim@^1.2.2: 1259 | version "1.3.1" 1260 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" 1261 | dependencies: 1262 | extsprintf "1.0.2" 1263 | json-schema "0.2.3" 1264 | verror "1.3.6" 1265 | 1266 | kew@~0.7.0: 1267 | version "0.7.0" 1268 | resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" 1269 | 1270 | kind-of@^3.0.2: 1271 | version "3.1.0" 1272 | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" 1273 | dependencies: 1274 | is-buffer "^1.0.2" 1275 | 1276 | klaw@^1.0.0: 1277 | version "1.3.1" 1278 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" 1279 | optionalDependencies: 1280 | graceful-fs "^4.1.9" 1281 | 1282 | lazy-cache@^1.0.3: 1283 | version "1.0.4" 1284 | resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" 1285 | 1286 | lazy-debug-legacy@0.0.X: 1287 | version "0.0.1" 1288 | resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" 1289 | 1290 | lcid@^1.0.0: 1291 | version "1.0.0" 1292 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" 1293 | dependencies: 1294 | invert-kv "^1.0.0" 1295 | 1296 | "less@2.6.x || ^2.7.1": 1297 | version "2.7.2" 1298 | resolved "https://registry.yarnpkg.com/less/-/less-2.7.2.tgz#368d6cc73e1fb03981183280918743c5dcf9b3df" 1299 | optionalDependencies: 1300 | errno "^0.1.1" 1301 | graceful-fs "^4.1.2" 1302 | image-size "~0.5.0" 1303 | mime "^1.2.11" 1304 | mkdirp "^0.5.0" 1305 | promise "^7.1.1" 1306 | request "^2.72.0" 1307 | source-map "^0.5.3" 1308 | 1309 | liftoff@^2.1.0: 1310 | version "2.3.0" 1311 | resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" 1312 | dependencies: 1313 | extend "^3.0.0" 1314 | findup-sync "^0.4.2" 1315 | fined "^1.0.1" 1316 | flagged-respawn "^0.3.2" 1317 | lodash.isplainobject "^4.0.4" 1318 | lodash.isstring "^4.0.1" 1319 | lodash.mapvalues "^4.4.0" 1320 | rechoir "^0.6.2" 1321 | resolve "^1.1.7" 1322 | 1323 | livereload-js@^2.2.0: 1324 | version "2.2.2" 1325 | resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.2.2.tgz#6c87257e648ab475bc24ea257457edcc1f8d0bc2" 1326 | 1327 | load-bmfont@^1.2.3: 1328 | version "1.3.0" 1329 | resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.3.0.tgz#bb7e7c710de6bcafcb13cb3b8c81e0c0131ecbc9" 1330 | dependencies: 1331 | buffer-equal "0.0.1" 1332 | mime "^1.3.4" 1333 | parse-bmfont-ascii "^1.0.3" 1334 | parse-bmfont-binary "^1.0.5" 1335 | parse-bmfont-xml "^1.1.0" 1336 | xhr "^2.0.1" 1337 | xtend "^4.0.0" 1338 | 1339 | lodash._baseassign@^3.0.0: 1340 | version "3.2.0" 1341 | resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" 1342 | dependencies: 1343 | lodash._basecopy "^3.0.0" 1344 | lodash.keys "^3.0.0" 1345 | 1346 | lodash._basecopy@^3.0.0: 1347 | version "3.0.1" 1348 | resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" 1349 | 1350 | lodash._basetostring@^3.0.0: 1351 | version "3.0.1" 1352 | resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" 1353 | 1354 | lodash._basevalues@^3.0.0: 1355 | version "3.0.0" 1356 | resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" 1357 | 1358 | lodash._bindcallback@^3.0.0: 1359 | version "3.0.1" 1360 | resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" 1361 | 1362 | lodash._createassigner@^3.0.0: 1363 | version "3.1.1" 1364 | resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" 1365 | dependencies: 1366 | lodash._bindcallback "^3.0.0" 1367 | lodash._isiterateecall "^3.0.0" 1368 | lodash.restparam "^3.0.0" 1369 | 1370 | lodash._getnative@^3.0.0: 1371 | version "3.9.1" 1372 | resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" 1373 | 1374 | lodash._isiterateecall@^3.0.0: 1375 | version "3.0.9" 1376 | resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" 1377 | 1378 | lodash._reescape@^3.0.0: 1379 | version "3.0.0" 1380 | resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" 1381 | 1382 | lodash._reevaluate@^3.0.0: 1383 | version "3.0.0" 1384 | resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" 1385 | 1386 | lodash._reinterpolate@^3.0.0: 1387 | version "3.0.0" 1388 | resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" 1389 | 1390 | lodash._root@^3.0.0: 1391 | version "3.0.1" 1392 | resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" 1393 | 1394 | lodash.assign@^3.0.0: 1395 | version "3.2.0" 1396 | resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" 1397 | dependencies: 1398 | lodash._baseassign "^3.0.0" 1399 | lodash._createassigner "^3.0.0" 1400 | lodash.keys "^3.0.0" 1401 | 1402 | lodash.assignwith@^4.0.7: 1403 | version "4.2.0" 1404 | resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" 1405 | 1406 | lodash.clone@^4.3.2: 1407 | version "4.5.0" 1408 | resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" 1409 | 1410 | lodash.defaults@^4.0.1: 1411 | version "4.2.0" 1412 | resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" 1413 | 1414 | lodash.escape@^3.0.0: 1415 | version "3.2.0" 1416 | resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" 1417 | dependencies: 1418 | lodash._root "^3.0.0" 1419 | 1420 | lodash.flatten@^4.2.0: 1421 | version "4.4.0" 1422 | resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" 1423 | 1424 | lodash.isarguments@^3.0.0: 1425 | version "3.1.0" 1426 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" 1427 | 1428 | lodash.isarray@^3.0.0: 1429 | version "3.0.4" 1430 | resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" 1431 | 1432 | lodash.isempty@^4.2.1: 1433 | version "4.4.0" 1434 | resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" 1435 | 1436 | lodash.isplainobject@^4.0.4: 1437 | version "4.0.6" 1438 | resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" 1439 | 1440 | lodash.isstring@^4.0.1: 1441 | version "4.0.1" 1442 | resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" 1443 | 1444 | lodash.keys@^3.0.0: 1445 | version "3.1.2" 1446 | resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" 1447 | dependencies: 1448 | lodash._getnative "^3.0.0" 1449 | lodash.isarguments "^3.0.0" 1450 | lodash.isarray "^3.0.0" 1451 | 1452 | lodash.mapvalues@^4.4.0: 1453 | version "4.6.0" 1454 | resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" 1455 | 1456 | lodash.merge@^4.4.0: 1457 | version "4.6.0" 1458 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" 1459 | 1460 | lodash.partialright@^4.1.4: 1461 | version "4.2.1" 1462 | resolved "https://registry.yarnpkg.com/lodash.partialright/-/lodash.partialright-4.2.1.tgz#0130d80e83363264d40074f329b8a3e7a8a1cc4b" 1463 | 1464 | lodash.pick@^4.2.1: 1465 | version "4.4.0" 1466 | resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" 1467 | 1468 | lodash.restparam@^3.0.0: 1469 | version "3.6.1" 1470 | resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" 1471 | 1472 | lodash.template@^3.0.0: 1473 | version "3.6.2" 1474 | resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" 1475 | dependencies: 1476 | lodash._basecopy "^3.0.0" 1477 | lodash._basetostring "^3.0.0" 1478 | lodash._basevalues "^3.0.0" 1479 | lodash._isiterateecall "^3.0.0" 1480 | lodash._reinterpolate "^3.0.0" 1481 | lodash.escape "^3.0.0" 1482 | lodash.keys "^3.0.0" 1483 | lodash.restparam "^3.0.0" 1484 | lodash.templatesettings "^3.0.0" 1485 | 1486 | lodash.templatesettings@^3.0.0: 1487 | version "3.1.1" 1488 | resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" 1489 | dependencies: 1490 | lodash._reinterpolate "^3.0.0" 1491 | lodash.escape "^3.0.0" 1492 | 1493 | lodash.uniq@^4.3.0: 1494 | version "4.5.0" 1495 | resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" 1496 | 1497 | lodash@^3.2.0: 1498 | version "3.10.1" 1499 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" 1500 | 1501 | lodash@^4.0.0: 1502 | version "4.17.4" 1503 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 1504 | 1505 | lodash@~1.0.1: 1506 | version "1.0.2" 1507 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" 1508 | 1509 | lodash@~2.4.1: 1510 | version "2.4.2" 1511 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" 1512 | 1513 | longest@^1.0.1: 1514 | version "1.0.1" 1515 | resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" 1516 | 1517 | lru-cache@2: 1518 | version "2.7.3" 1519 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" 1520 | 1521 | map-cache@^0.2.0: 1522 | version "0.2.2" 1523 | resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" 1524 | 1525 | map-stream@~0.1.0: 1526 | version "0.1.0" 1527 | resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" 1528 | 1529 | media-typer@0.3.0: 1530 | version "0.3.0" 1531 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 1532 | 1533 | merge-defaults@^0.2.1: 1534 | version "0.2.1" 1535 | resolved "https://registry.yarnpkg.com/merge-defaults/-/merge-defaults-0.2.1.tgz#dd42248eb96bb6a51521724321c72ff9583dde80" 1536 | dependencies: 1537 | lodash "~2.4.1" 1538 | 1539 | micromatch@^2.3.7: 1540 | version "2.3.11" 1541 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" 1542 | dependencies: 1543 | arr-diff "^2.0.0" 1544 | array-unique "^0.2.1" 1545 | braces "^1.8.2" 1546 | expand-brackets "^0.1.4" 1547 | extglob "^0.3.1" 1548 | filename-regex "^2.0.0" 1549 | is-extglob "^1.0.0" 1550 | is-glob "^2.0.1" 1551 | kind-of "^3.0.2" 1552 | normalize-path "^2.0.1" 1553 | object.omit "^2.0.0" 1554 | parse-glob "^3.0.4" 1555 | regex-cache "^0.4.2" 1556 | 1557 | mime-db@~1.26.0: 1558 | version "1.26.0" 1559 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" 1560 | 1561 | mime-types@^2.1.12, mime-types@~2.1.13, mime-types@~2.1.7: 1562 | version "2.1.14" 1563 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" 1564 | dependencies: 1565 | mime-db "~1.26.0" 1566 | 1567 | mime@^1.2.11, mime@^1.3.4: 1568 | version "1.3.4" 1569 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 1570 | 1571 | min-document@^2.19.0: 1572 | version "2.19.0" 1573 | resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" 1574 | dependencies: 1575 | dom-walk "^0.1.0" 1576 | 1577 | mini-lr@^0.1.8: 1578 | version "0.1.9" 1579 | resolved "https://registry.yarnpkg.com/mini-lr/-/mini-lr-0.1.9.tgz#02199d27347953d1fd1d6dbded4261f187b2d0f6" 1580 | dependencies: 1581 | body-parser "~1.14.0" 1582 | debug "^2.2.0" 1583 | faye-websocket "~0.7.2" 1584 | livereload-js "^2.2.0" 1585 | parseurl "~1.3.0" 1586 | qs "~2.2.3" 1587 | 1588 | minimatch@^2.0.1: 1589 | version "2.0.10" 1590 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" 1591 | dependencies: 1592 | brace-expansion "^1.0.0" 1593 | 1594 | minimatch@^3.0.2: 1595 | version "3.0.3" 1596 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" 1597 | dependencies: 1598 | brace-expansion "^1.0.0" 1599 | 1600 | minimatch@~0.2.11: 1601 | version "0.2.14" 1602 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" 1603 | dependencies: 1604 | lru-cache "2" 1605 | sigmund "~1.0.0" 1606 | 1607 | minimist@0.0.8: 1608 | version "0.0.8" 1609 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1610 | 1611 | minimist@^1.1.0: 1612 | version "1.2.0" 1613 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 1614 | 1615 | mkdirp@0.5.0: 1616 | version "0.5.0" 1617 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" 1618 | dependencies: 1619 | minimist "0.0.8" 1620 | 1621 | mkdirp@^0.5.0, mkdirp@^0.5.1: 1622 | version "0.5.1" 1623 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 1624 | dependencies: 1625 | minimist "0.0.8" 1626 | 1627 | ms@0.7.1: 1628 | version "0.7.1" 1629 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" 1630 | 1631 | ms@0.7.2: 1632 | version "0.7.2" 1633 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 1634 | 1635 | multipipe@^0.1.2: 1636 | version "0.1.2" 1637 | resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" 1638 | dependencies: 1639 | duplexer2 "0.0.2" 1640 | 1641 | natives@^1.1.0: 1642 | version "1.1.0" 1643 | resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.0.tgz#e9ff841418a6b2ec7a495e939984f78f163e6e31" 1644 | 1645 | node-rest-client@^1.5.1: 1646 | version "1.8.0" 1647 | resolved "https://registry.yarnpkg.com/node-rest-client/-/node-rest-client-1.8.0.tgz#8d3c566b817e27394cb7273783a41caefe3e5955" 1648 | dependencies: 1649 | debug "~2.2.0" 1650 | xml2js ">=0.2.4" 1651 | 1652 | normalize-path@^2.0.1: 1653 | version "2.0.1" 1654 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" 1655 | 1656 | nth-check@~1.0.0: 1657 | version "1.0.1" 1658 | resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" 1659 | dependencies: 1660 | boolbase "~1.0.0" 1661 | 1662 | number-is-nan@^1.0.0: 1663 | version "1.0.1" 1664 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1665 | 1666 | oauth-sign@~0.8.1: 1667 | version "0.8.2" 1668 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" 1669 | 1670 | object-assign@4.1.0: 1671 | version "4.1.0" 1672 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" 1673 | 1674 | object-assign@^3.0.0: 1675 | version "3.0.0" 1676 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" 1677 | 1678 | object-assign@^4.0.1: 1679 | version "4.1.1" 1680 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1681 | 1682 | object.omit@^2.0.0: 1683 | version "2.0.1" 1684 | resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" 1685 | dependencies: 1686 | for-own "^0.1.4" 1687 | is-extendable "^0.1.1" 1688 | 1689 | on-finished@~2.3.0: 1690 | version "2.3.0" 1691 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 1692 | dependencies: 1693 | ee-first "1.1.1" 1694 | 1695 | once@^1.3.0, once@~1.3.0: 1696 | version "1.3.3" 1697 | resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" 1698 | dependencies: 1699 | wrappy "1" 1700 | 1701 | orchestrator@^0.3.0: 1702 | version "0.3.8" 1703 | resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" 1704 | dependencies: 1705 | end-of-stream "~0.1.5" 1706 | sequencify "~0.0.7" 1707 | stream-consume "~0.1.0" 1708 | 1709 | ordered-read-streams@^0.1.0: 1710 | version "0.1.0" 1711 | resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" 1712 | 1713 | os-homedir@^1.0.0, os-homedir@^1.0.1: 1714 | version "1.0.2" 1715 | resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 1716 | 1717 | os-locale@^1.4.0: 1718 | version "1.4.0" 1719 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" 1720 | dependencies: 1721 | lcid "^1.0.0" 1722 | 1723 | parse-bmfont-ascii@^1.0.3: 1724 | version "1.0.6" 1725 | resolved "https://registry.yarnpkg.com/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz#11ac3c3ff58f7c2020ab22769079108d4dfa0285" 1726 | 1727 | parse-bmfont-binary@^1.0.5: 1728 | version "1.0.6" 1729 | resolved "https://registry.yarnpkg.com/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz#d038b476d3e9dd9db1e11a0b0e53a22792b69006" 1730 | 1731 | parse-bmfont-xml@^1.1.0: 1732 | version "1.1.3" 1733 | resolved "https://registry.yarnpkg.com/parse-bmfont-xml/-/parse-bmfont-xml-1.1.3.tgz#d6b66a371afd39c5007d9f0eeb262a4f2cce7b7c" 1734 | dependencies: 1735 | xml-parse-from-string "^1.0.0" 1736 | xml2js "^0.4.5" 1737 | 1738 | parse-filepath@^1.0.1: 1739 | version "1.0.1" 1740 | resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" 1741 | dependencies: 1742 | is-absolute "^0.2.3" 1743 | map-cache "^0.2.0" 1744 | path-root "^0.1.1" 1745 | 1746 | parse-glob@^3.0.4: 1747 | version "3.0.4" 1748 | resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" 1749 | dependencies: 1750 | glob-base "^0.3.0" 1751 | is-dotfile "^1.0.0" 1752 | is-extglob "^1.0.0" 1753 | is-glob "^2.0.0" 1754 | 1755 | parse-headers@^2.0.0: 1756 | version "2.0.1" 1757 | resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.1.tgz#6ae83a7aa25a9d9b700acc28698cd1f1ed7e9536" 1758 | dependencies: 1759 | for-each "^0.3.2" 1760 | trim "0.0.1" 1761 | 1762 | parse-passwd@^1.0.0: 1763 | version "1.0.0" 1764 | resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" 1765 | 1766 | parse-png@^1.0.0, parse-png@^1.1.1: 1767 | version "1.1.1" 1768 | resolved "https://registry.yarnpkg.com/parse-png/-/parse-png-1.1.1.tgz#0633f18e0e674dbcddf9f3d74f7df6aed8312418" 1769 | dependencies: 1770 | pngjs "^2.2.0" 1771 | 1772 | parseurl@~1.3.0: 1773 | version "1.3.1" 1774 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" 1775 | 1776 | path-is-absolute@^1.0.0: 1777 | version "1.0.1" 1778 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1779 | 1780 | path-root-regex@^0.1.0: 1781 | version "0.1.2" 1782 | resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" 1783 | 1784 | path-root@^0.1.1: 1785 | version "0.1.1" 1786 | resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" 1787 | dependencies: 1788 | path-root-regex "^0.1.0" 1789 | 1790 | pause-stream@0.0.11: 1791 | version "0.0.11" 1792 | resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" 1793 | dependencies: 1794 | through "~2.3" 1795 | 1796 | pend@~1.2.0: 1797 | version "1.2.0" 1798 | resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 1799 | 1800 | phantomjs-prebuilt@^2.1.10: 1801 | version "2.1.14" 1802 | resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0" 1803 | dependencies: 1804 | es6-promise "~4.0.3" 1805 | extract-zip "~1.5.0" 1806 | fs-extra "~1.0.0" 1807 | hasha "~2.2.0" 1808 | kew "~0.7.0" 1809 | progress "~1.1.8" 1810 | request "~2.79.0" 1811 | request-progress "~2.0.1" 1812 | which "~1.2.10" 1813 | 1814 | pinkie-promise@^2.0.0: 1815 | version "2.0.1" 1816 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1817 | dependencies: 1818 | pinkie "^2.0.0" 1819 | 1820 | pinkie@^2.0.0: 1821 | version "2.0.4" 1822 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1823 | 1824 | pixelmatch@^4.0.0: 1825 | version "4.0.2" 1826 | resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854" 1827 | dependencies: 1828 | pngjs "^3.0.0" 1829 | 1830 | pn@^1.0.0: 1831 | version "1.0.0" 1832 | resolved "https://registry.yarnpkg.com/pn/-/pn-1.0.0.tgz#1cf5a30b0d806cd18f88fc41a6b5d4ad615b3ba9" 1833 | 1834 | pngjs@^2.2.0: 1835 | version "2.3.1" 1836 | resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-2.3.1.tgz#11d1e12b9cb64d63e30c143a330f4c1f567da85f" 1837 | 1838 | pngjs@^3.0.0: 1839 | version "3.0.1" 1840 | resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.0.1.tgz#b15086ac1ac47298c8fd3f9cdf364fa9879c4db6" 1841 | 1842 | preserve@^0.2.0: 1843 | version "0.2.0" 1844 | resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" 1845 | 1846 | pretty-hrtime@^1.0.0: 1847 | version "1.0.3" 1848 | resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" 1849 | 1850 | process-nextick-args@~1.0.6: 1851 | version "1.0.7" 1852 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 1853 | 1854 | process@~0.5.1: 1855 | version "0.5.2" 1856 | resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" 1857 | 1858 | progress@~1.1.8: 1859 | version "1.1.8" 1860 | resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" 1861 | 1862 | promise@^7.1.1: 1863 | version "7.1.1" 1864 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" 1865 | dependencies: 1866 | asap "~2.0.3" 1867 | 1868 | prr@~0.0.0: 1869 | version "0.0.0" 1870 | resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" 1871 | 1872 | punycode@^1.4.1: 1873 | version "1.4.1" 1874 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 1875 | 1876 | qs@5.2.0: 1877 | version "5.2.0" 1878 | resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" 1879 | 1880 | qs@~2.2.3: 1881 | version "2.2.5" 1882 | resolved "https://registry.yarnpkg.com/qs/-/qs-2.2.5.tgz#1088abaf9dcc0ae5ae45b709e6c6b5888b23923c" 1883 | 1884 | qs@~6.3.0: 1885 | version "6.3.1" 1886 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.1.tgz#918c0b3bcd36679772baf135b1acb4c1651ed79d" 1887 | 1888 | randomatic@^1.1.3: 1889 | version "1.1.6" 1890 | resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" 1891 | dependencies: 1892 | is-number "^2.0.2" 1893 | kind-of "^3.0.2" 1894 | 1895 | raw-body@~2.1.5: 1896 | version "2.1.7" 1897 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" 1898 | dependencies: 1899 | bytes "2.4.0" 1900 | iconv-lite "0.4.13" 1901 | unpipe "1.0.0" 1902 | 1903 | read-chunk@^1.0.1: 1904 | version "1.0.1" 1905 | resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194" 1906 | 1907 | readable-stream@1.1, readable-stream@~1.1.9: 1908 | version "1.1.14" 1909 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 1910 | dependencies: 1911 | core-util-is "~1.0.0" 1912 | inherits "~2.0.1" 1913 | isarray "0.0.1" 1914 | string_decoder "~0.10.x" 1915 | 1916 | "readable-stream@>=1.0.33-1 <1.1.0-0": 1917 | version "1.0.34" 1918 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" 1919 | dependencies: 1920 | core-util-is "~1.0.0" 1921 | inherits "~2.0.1" 1922 | isarray "0.0.1" 1923 | string_decoder "~0.10.x" 1924 | 1925 | readable-stream@^2.1.5: 1926 | version "2.2.2" 1927 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" 1928 | dependencies: 1929 | buffer-shims "^1.0.0" 1930 | core-util-is "~1.0.0" 1931 | inherits "~2.0.1" 1932 | isarray "~1.0.0" 1933 | process-nextick-args "~1.0.6" 1934 | string_decoder "~0.10.x" 1935 | util-deprecate "~1.0.1" 1936 | 1937 | readable-stream@~2.0.0: 1938 | version "2.0.6" 1939 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" 1940 | dependencies: 1941 | core-util-is "~1.0.0" 1942 | inherits "~2.0.1" 1943 | isarray "~1.0.0" 1944 | process-nextick-args "~1.0.6" 1945 | string_decoder "~0.10.x" 1946 | util-deprecate "~1.0.1" 1947 | 1948 | rechoir@^0.6.2: 1949 | version "0.6.2" 1950 | resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" 1951 | dependencies: 1952 | resolve "^1.1.6" 1953 | 1954 | regex-cache@^0.4.2: 1955 | version "0.4.3" 1956 | resolved "http://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" 1957 | dependencies: 1958 | is-equal-shallow "^0.1.3" 1959 | is-primitive "^2.0.0" 1960 | 1961 | repeat-element@^1.1.2: 1962 | version "1.1.2" 1963 | resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" 1964 | 1965 | repeat-string@^1.5.2: 1966 | version "1.6.1" 1967 | resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" 1968 | 1969 | replace-ext@0.0.1: 1970 | version "0.0.1" 1971 | resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" 1972 | 1973 | request-progress@~2.0.1: 1974 | version "2.0.1" 1975 | resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" 1976 | dependencies: 1977 | throttleit "^1.0.0" 1978 | 1979 | request@^2.65.0, request@^2.72.0, request@~2.79.0: 1980 | version "2.79.0" 1981 | resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" 1982 | dependencies: 1983 | aws-sign2 "~0.6.0" 1984 | aws4 "^1.2.1" 1985 | caseless "~0.11.0" 1986 | combined-stream "~1.0.5" 1987 | extend "~3.0.0" 1988 | forever-agent "~0.6.1" 1989 | form-data "~2.1.1" 1990 | har-validator "~2.0.6" 1991 | hawk "~3.1.3" 1992 | http-signature "~1.1.0" 1993 | is-typedarray "~1.0.0" 1994 | isstream "~0.1.2" 1995 | json-stringify-safe "~5.0.1" 1996 | mime-types "~2.1.7" 1997 | oauth-sign "~0.8.1" 1998 | qs "~6.3.0" 1999 | stringstream "~0.0.4" 2000 | tough-cookie "~2.3.0" 2001 | tunnel-agent "~0.4.1" 2002 | uuid "^3.0.0" 2003 | 2004 | require-directory@^2.1.1: 2005 | version "2.1.1" 2006 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 2007 | 2008 | resize-img@^1.1.0: 2009 | version "1.1.2" 2010 | resolved "https://registry.yarnpkg.com/resize-img/-/resize-img-1.1.2.tgz#fad650faf3ef2c53ea63112bc272d95e9d92550e" 2011 | dependencies: 2012 | bmp-js "0.0.1" 2013 | file-type "^3.8.0" 2014 | get-stream "^2.0.0" 2015 | jimp "^0.2.21" 2016 | jpeg-js "^0.1.1" 2017 | parse-png "^1.1.1" 2018 | 2019 | resolve-dir@^0.1.0: 2020 | version "0.1.1" 2021 | resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" 2022 | dependencies: 2023 | expand-tilde "^1.2.2" 2024 | global-modules "^0.2.3" 2025 | 2026 | resolve-url@~0.2.1: 2027 | version "0.2.1" 2028 | resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" 2029 | 2030 | resolve@^1.1.6, resolve@^1.1.7: 2031 | version "1.2.0" 2032 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" 2033 | 2034 | right-align@^0.1.1: 2035 | version "0.1.3" 2036 | resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" 2037 | dependencies: 2038 | align-text "^0.1.1" 2039 | 2040 | sax@>=0.6.0: 2041 | version "1.2.2" 2042 | resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" 2043 | 2044 | semver@^4.1.0: 2045 | version "4.3.6" 2046 | resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" 2047 | 2048 | semver@^5.3.0: 2049 | version "5.3.0" 2050 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" 2051 | 2052 | sequencify@~0.0.7: 2053 | version "0.0.7" 2054 | resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" 2055 | 2056 | sigmund@~1.0.0: 2057 | version "1.0.1" 2058 | resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" 2059 | 2060 | sntp@1.x.x: 2061 | version "1.0.9" 2062 | resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" 2063 | dependencies: 2064 | hoek "2.x.x" 2065 | 2066 | source-map-resolve@^0.3.0: 2067 | version "0.3.1" 2068 | resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" 2069 | dependencies: 2070 | atob "~1.1.0" 2071 | resolve-url "~0.2.1" 2072 | source-map-url "~0.3.0" 2073 | urix "~0.1.0" 2074 | 2075 | source-map-url@~0.3.0: 2076 | version "0.3.0" 2077 | resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" 2078 | 2079 | source-map@0.X, source-map@^0.5.1, source-map@^0.5.3, source-map@~0.5.1: 2080 | version "0.5.6" 2081 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" 2082 | 2083 | source-map@^0.1.38: 2084 | version "0.1.43" 2085 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" 2086 | dependencies: 2087 | amdefine ">=0.0.4" 2088 | 2089 | sparkles@^1.0.0: 2090 | version "1.0.0" 2091 | resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" 2092 | 2093 | split@0.3: 2094 | version "0.3.3" 2095 | resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" 2096 | dependencies: 2097 | through "2" 2098 | 2099 | sshpk@^1.7.0: 2100 | version "1.10.2" 2101 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" 2102 | dependencies: 2103 | asn1 "~0.2.3" 2104 | assert-plus "^1.0.0" 2105 | dashdash "^1.12.0" 2106 | getpass "^0.1.1" 2107 | optionalDependencies: 2108 | bcrypt-pbkdf "^1.0.0" 2109 | ecc-jsbn "~0.1.1" 2110 | jodid25519 "^1.0.0" 2111 | jsbn "~0.1.0" 2112 | tweetnacl "~0.14.0" 2113 | 2114 | statuses@1: 2115 | version "1.3.1" 2116 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" 2117 | 2118 | stream-combiner@~0.0.4: 2119 | version "0.0.4" 2120 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" 2121 | dependencies: 2122 | duplexer "~0.1.1" 2123 | 2124 | stream-consume@~0.1.0: 2125 | version "0.1.0" 2126 | resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" 2127 | 2128 | stream-to-buffer@^0.1.0: 2129 | version "0.1.0" 2130 | resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9" 2131 | dependencies: 2132 | stream-to "~0.2.0" 2133 | 2134 | stream-to@~0.2.0: 2135 | version "0.2.2" 2136 | resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d" 2137 | 2138 | string-width@^1.0.1: 2139 | version "1.0.2" 2140 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 2141 | dependencies: 2142 | code-point-at "^1.0.0" 2143 | is-fullwidth-code-point "^1.0.0" 2144 | strip-ansi "^3.0.0" 2145 | 2146 | string_decoder@~0.10.x: 2147 | version "0.10.31" 2148 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 2149 | 2150 | stringstream@~0.0.4: 2151 | version "0.0.5" 2152 | resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" 2153 | 2154 | strip-ansi@^0.3.0: 2155 | version "0.3.0" 2156 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" 2157 | dependencies: 2158 | ansi-regex "^0.2.1" 2159 | 2160 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 2161 | version "3.0.1" 2162 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 2163 | dependencies: 2164 | ansi-regex "^2.0.0" 2165 | 2166 | strip-bom@3.X: 2167 | version "3.0.0" 2168 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 2169 | 2170 | strip-bom@^1.0.0: 2171 | version "1.0.0" 2172 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" 2173 | dependencies: 2174 | first-chunk-stream "^1.0.0" 2175 | is-utf8 "^0.2.0" 2176 | 2177 | supports-color@^0.2.0: 2178 | version "0.2.0" 2179 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" 2180 | 2181 | supports-color@^2.0.0: 2182 | version "2.0.0" 2183 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 2184 | 2185 | svg2png@~3.0.1: 2186 | version "3.0.1" 2187 | resolved "https://registry.yarnpkg.com/svg2png/-/svg2png-3.0.1.tgz#a2644d68b0231ac00af431aa163714ff17106447" 2188 | dependencies: 2189 | phantomjs-prebuilt "^2.1.10" 2190 | pn "^1.0.0" 2191 | yargs "^3.31.0" 2192 | 2193 | throttleit@^1.0.0: 2194 | version "1.0.0" 2195 | resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" 2196 | 2197 | through2@2.X, through2@^2.0.0: 2198 | version "2.0.3" 2199 | resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" 2200 | dependencies: 2201 | readable-stream "^2.1.5" 2202 | xtend "~4.0.1" 2203 | 2204 | through2@^0.6.1: 2205 | version "0.6.5" 2206 | resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" 2207 | dependencies: 2208 | readable-stream ">=1.0.33-1 <1.1.0-0" 2209 | xtend ">=4.0.0 <4.1.0-0" 2210 | 2211 | through@2, through@~2.3, through@~2.3.1: 2212 | version "2.3.8" 2213 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 2214 | 2215 | tildify@^1.0.0: 2216 | version "1.2.0" 2217 | resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" 2218 | dependencies: 2219 | os-homedir "^1.0.0" 2220 | 2221 | time-stamp@^1.0.0: 2222 | version "1.0.1" 2223 | resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" 2224 | 2225 | tinycolor2@^1.1.2: 2226 | version "1.4.1" 2227 | resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" 2228 | 2229 | to-ico@^1.1.2: 2230 | version "1.1.3" 2231 | resolved "https://registry.yarnpkg.com/to-ico/-/to-ico-1.1.3.tgz#59f1774042f6732f690d79744ade985b7998db77" 2232 | dependencies: 2233 | arrify "^1.0.1" 2234 | icojs "^0.4.1" 2235 | image-size "^0.5.0" 2236 | parse-png "^1.0.0" 2237 | resize-img "^1.1.0" 2238 | 2239 | tough-cookie@~2.3.0: 2240 | version "2.3.2" 2241 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" 2242 | dependencies: 2243 | punycode "^1.4.1" 2244 | 2245 | trim@0.0.1: 2246 | version "0.0.1" 2247 | resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" 2248 | 2249 | tunnel-agent@~0.4.1: 2250 | version "0.4.3" 2251 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" 2252 | 2253 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: 2254 | version "0.14.5" 2255 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 2256 | 2257 | type-is@~1.6.10: 2258 | version "1.6.14" 2259 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" 2260 | dependencies: 2261 | media-typer "0.3.0" 2262 | mime-types "~2.1.13" 2263 | 2264 | typedarray@~0.0.5: 2265 | version "0.0.6" 2266 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 2267 | 2268 | uglify-js@^2.7.0: 2269 | version "2.7.5" 2270 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" 2271 | dependencies: 2272 | async "~0.2.6" 2273 | source-map "~0.5.1" 2274 | uglify-to-browserify "~1.0.0" 2275 | yargs "~3.10.0" 2276 | 2277 | uglify-to-browserify@~1.0.0: 2278 | version "1.0.2" 2279 | resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" 2280 | 2281 | unc-path-regex@^0.1.0: 2282 | version "0.1.2" 2283 | resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" 2284 | 2285 | underscore@^1.8.3: 2286 | version "1.8.3" 2287 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" 2288 | 2289 | unique-stream@^1.0.0: 2290 | version "1.0.0" 2291 | resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" 2292 | 2293 | unpipe@1.0.0: 2294 | version "1.0.0" 2295 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 2296 | 2297 | urix@^0.1.0, urix@~0.1.0: 2298 | version "0.1.0" 2299 | resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" 2300 | 2301 | url-regex@^3.0.0: 2302 | version "3.2.0" 2303 | resolved "https://registry.yarnpkg.com/url-regex/-/url-regex-3.2.0.tgz#dbad1e0c9e29e105dd0b1f09f6862f7fdb482724" 2304 | dependencies: 2305 | ip-regex "^1.0.1" 2306 | 2307 | user-home@^1.1.1: 2308 | version "1.1.1" 2309 | resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" 2310 | 2311 | util-deprecate@~1.0.1: 2312 | version "1.0.2" 2313 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 2314 | 2315 | uuid@^3.0.0: 2316 | version "3.0.1" 2317 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" 2318 | 2319 | v8flags@^2.0.2: 2320 | version "2.0.11" 2321 | resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" 2322 | dependencies: 2323 | user-home "^1.1.1" 2324 | 2325 | verror@1.3.6: 2326 | version "1.3.6" 2327 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" 2328 | dependencies: 2329 | extsprintf "1.0.2" 2330 | 2331 | vinyl-fs@^0.3.0: 2332 | version "0.3.14" 2333 | resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" 2334 | dependencies: 2335 | defaults "^1.0.0" 2336 | glob-stream "^3.1.5" 2337 | glob-watcher "^0.0.6" 2338 | graceful-fs "^3.0.0" 2339 | mkdirp "^0.5.0" 2340 | strip-bom "^1.0.0" 2341 | through2 "^0.6.1" 2342 | vinyl "^0.4.0" 2343 | 2344 | vinyl-sourcemaps-apply@^0.2.0: 2345 | version "0.2.1" 2346 | resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" 2347 | dependencies: 2348 | source-map "^0.5.1" 2349 | 2350 | vinyl@1.X, vinyl@^1.1.0: 2351 | version "1.2.0" 2352 | resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" 2353 | dependencies: 2354 | clone "^1.0.0" 2355 | clone-stats "^0.0.1" 2356 | replace-ext "0.0.1" 2357 | 2358 | vinyl@^0.4.0: 2359 | version "0.4.6" 2360 | resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" 2361 | dependencies: 2362 | clone "^0.2.0" 2363 | clone-stats "^0.0.1" 2364 | 2365 | vinyl@^0.5.0: 2366 | version "0.5.3" 2367 | resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" 2368 | dependencies: 2369 | clone "^1.0.0" 2370 | clone-stats "^0.0.1" 2371 | replace-ext "0.0.1" 2372 | 2373 | websocket-driver@>=0.3.6: 2374 | version "0.6.5" 2375 | resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" 2376 | dependencies: 2377 | websocket-extensions ">=0.1.1" 2378 | 2379 | websocket-extensions@>=0.1.1: 2380 | version "0.1.1" 2381 | resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" 2382 | 2383 | when@^3.7.7: 2384 | version "3.7.7" 2385 | resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" 2386 | 2387 | which@^1.2.12, which@~1.2.10: 2388 | version "1.2.12" 2389 | resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" 2390 | dependencies: 2391 | isexe "^1.1.1" 2392 | 2393 | window-size@0.1.0: 2394 | version "0.1.0" 2395 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" 2396 | 2397 | window-size@^0.1.4: 2398 | version "0.1.4" 2399 | resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" 2400 | 2401 | wordwrap@0.0.2: 2402 | version "0.0.2" 2403 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" 2404 | 2405 | wrap-ansi@^2.0.0: 2406 | version "2.1.0" 2407 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" 2408 | dependencies: 2409 | string-width "^1.0.1" 2410 | strip-ansi "^3.0.1" 2411 | 2412 | wrappy@1: 2413 | version "1.0.2" 2414 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 2415 | 2416 | xhr@^2.0.1: 2417 | version "2.4.0" 2418 | resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.4.0.tgz#e16e66a45f869861eeefab416d5eff722dc40993" 2419 | dependencies: 2420 | global "~4.3.0" 2421 | is-function "^1.0.1" 2422 | parse-headers "^2.0.0" 2423 | xtend "^4.0.0" 2424 | 2425 | xml-parse-from-string@^1.0.0: 2426 | version "1.0.0" 2427 | resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.0.tgz#feba5809f3cd2d17d2e4239fa810cd0319fc5da5" 2428 | 2429 | xml2js@>=0.2.4, xml2js@^0.4.5: 2430 | version "0.4.17" 2431 | resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" 2432 | dependencies: 2433 | sax ">=0.6.0" 2434 | xmlbuilder "^4.1.0" 2435 | 2436 | xmlbuilder@^4.1.0: 2437 | version "4.2.1" 2438 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" 2439 | dependencies: 2440 | lodash "^4.0.0" 2441 | 2442 | "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.1: 2443 | version "4.0.1" 2444 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" 2445 | 2446 | y18n@^3.2.0: 2447 | version "3.2.1" 2448 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" 2449 | 2450 | yargs@^3.31.0: 2451 | version "3.32.0" 2452 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" 2453 | dependencies: 2454 | camelcase "^2.0.1" 2455 | cliui "^3.0.3" 2456 | decamelize "^1.1.1" 2457 | os-locale "^1.4.0" 2458 | string-width "^1.0.1" 2459 | window-size "^0.1.4" 2460 | y18n "^3.2.0" 2461 | 2462 | yargs@~3.10.0: 2463 | version "3.10.0" 2464 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" 2465 | dependencies: 2466 | camelcase "^1.0.2" 2467 | cliui "^2.1.0" 2468 | decamelize "^1.0.0" 2469 | window-size "0.1.0" 2470 | 2471 | yauzl@2.4.1: 2472 | version "2.4.1" 2473 | resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" 2474 | dependencies: 2475 | fd-slicer "~1.0.1" 2476 | --------------------------------------------------------------------------------