diff --git a/.gitignore b/.gitignore index 2452096..115a25d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ +/coverage /.idea/ -/node_modules +**/node_modules .gtt.yml .DS_STORE +yarn-error.log +**/._* +out diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..71d9c2d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: node_js +node_js: + - "6" + - "6.11" + - "8" + - "10" +cache: yarn +script: +- yarn build +deploy: + provider: releases + api_key: + secure: "q1kPg8zNInr1zrEsFvuX4r/Sp0DntK9m8uIi6PqpFNci3HnKEx6aLTe6eVaDpBfMyk+OqAxhyyytDmH36OhfEeDFzuR4wnzRM6LI0pJa9faWohY8CXke2/V6mWRGDorSDKUJ2FriJGL4p/hE2jw1ms/tQh7EsYE1ch3HcIKwkuHKhOVw84I4wW0VNHBMQ/RSnWnUENVO9Tfj/jHlZyltADAgJxRhQd9xg4lTyy95lMoM1eTjyrUiiVg9MPxLj8Mzv5mXbRCASxTziS/FBPrOOB8sMIyfN7cL8/e8hfMTuiWBmfWNaRrQTenLpm1IXobV8oANKg6DqS+cawN/zp9AIXsfzOksQib2tswbCBNY9Vj3tNSTpTLuyTFVvvlB/vNB715oTJX0cuHp8e4nW6AqP1DKUTTELwR1lji0YyGLIFQEw9RcTBP0e3LHXuXjgE80iypsZQQVNIHgIiNNvc1VEMJYECkeHsXt5LblMoJcw+tI7JL6LL3gv3g9j/3/dLvlfTBiXJPTxTlPl0zFvM31t4edGkmfVIRFxjpCGnrOfAnclmDt/r8u3A4i29FovbfIRTkEA+naQQWJvjXmkqTTBbAtOCFROT23EcydEoaE2uYI6Dc0AGPnoeZ3NSTRNvT2SjnotyXT0vVFfDIuB4FLtrX7kNEtHvNkBNzptS28i8E=" + file_glob: true + file: out/* + skip_cleanup: true + on: + tags: true +after_success: +- yarn run coveralls diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..73931f4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing + +## How to test the basic functions of gtt + +Create a new project on GitLab if you haven't done so already and write down the project path. e.g. `kriskbx/example-project`. To test groups and subgroups, create a new group with at least one subgroup. Add a new project to both the parent group (e.g. `example-group`) and the subgroup (e.g. `example-group/example-subgroup`) and write down the paths of all the projects and groups. Create at least two issues (write down their id e.g. `42`, `43`) and one merge request (e.g. `13`) in all projects. Or just use our public group over here: https://gitlab.com/gtt + +Then run the following basic commands and check that they work as expected: + +``` +# Test config command +gtt config + +# Test basic time tracking +gtt start "kriskbx/example-project" 42 +gtt stop +gtt log + +# Test cancelling +gtt start "kriskbx/example-project" 42 +gtt cancel +gtt log + +# Test merge request +gtt start --type=merge_request "kriskbx/example-project" 13 +gtt stop +gtt log + +# Test issue creation +gtt create "krisbxkbx/example-project" "New Issue" +gtt stop +gtt log + +# Test editing +gtt edit + +# Test deletion +gtt start "kriskbx/example-project" 42 +gtt stop +gtt delete + +# Test sync, check out issues on GitLab if changes are synced correctly +gtt sync + +# Test basic report +gtt report "kriskbx/example-project" + +# Test report for a single issue and multiple issues +gtt report "kriskbx/example-project" 42 +gtt report "kriskbx/example-project" 42 43 + +# Test report for a group +gtt report --type=group "example-group" +gtt report --type=group --subgroups "example-group" + +# Test combined reports +gtt report "kriskbx/example-project" "example-group/example-project" + +# Test outputs +gtt report --output=table "kriskbx/example-project" +gtt report --output=markdown "kriskbx/example-project" +gtt report --output=csv "kriskbx/example-project" +gtt report --output=pdf --file=test.pdf "kriskbx/example-project" +gtt report --output=xlsx --file=test.xlsx "kriskbx/example-project" + +# Test timeframes (adjust the values so it includes/excludes your issues) +gtt report --from="2020-06-02" --to="2020-06-03" "kriskbx/example-project" + +# Test filtering (you might need to add milestones, labels and additional stuff to properly test this) +gtt report --closed "kriskbx/example-project" +gtt report --user=username "kriskbx/example-project" +gtt report --milestone=milestone_name "kriskbx/example-project" +gtt report --include_by_labels=critical "kriskbx/example-project" +gtt report --exclude_by_labels=ignore "kriskbx/example-project" +gtt report --query=issues "kriskbx/example-project" +``` diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index 6c49a64..2d9509f --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ FROM node:8.2.1-alpine -ENV GTT_VERSION 1.5.5 +ENV GTT_VERSION 1.7.39 +ENV EDITOR vi + +WORKDIR /pwd RUN yarn global add --prefix /usr/local "gitlab-time-tracker@$GTT_VERSION" -VOLUME ["/root"] +VOLUME ["/root", "/pwd"] ENTRYPOINT ["gtt"] CMD ["--help"] diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/documentation.md b/documentation.md new file mode 100644 index 0000000..41fe224 --- /dev/null +++ b/documentation.md @@ -0,0 +1,829 @@ +# gtt documentation + +## contents + +* [requirements](#requirements) +* [installation](#installation) +* [updating](#updating) +* [docker](#docker) +* [commands](#commands) + * [I) configuration](#i-configuration) + * [II) time tracking](#ii-time-tracking) + * [III) reports](#iii-reports) +* [configuration](#configuration) + * [config file](#config-file) + * [time format](#time-format) +* [how to use gtt as a library](#how-to-use-gtt-as-a-library) +* [dumps](#dumps) +* [faqs](#faqs) +* [contributing](#contributing) +* [support further development 🍺](#support-further-development) +* [license](#license) + +## requirements + +* [node.js](https://nodejs.org/en/download) version >= 6 +* [npm](https://github.com/npm/npm) or [yarn](https://yarnpkg.com/en/docs/install) + +## installation + +Install the gtt command line interface using yarn: + +```shell +yarn global add gitlab-time-tracker --prefix /usr/local +``` + +... or download a compiled binary from [here](https://github.com/kriskbx/gitlab-time-tracker/releases) and put it into your `PATH`. + +Run the config command to create a config file and open it in your default editor. +In linux terminal, you must set your preferred editor in the environment. For example, use `export EDITOR=vim` to edit the files with vim (put this in `.bashrc` or similar to have it always configured). +If nothing happens, open the file manually: `~/.local/share/.gtt` - on Windows: `C:\Users\YourUserName\.gtt\config.yml` + +```shell +gtt config +``` + +Add your GitLab API url and your [GitLab API personal token](https://gitlab.com/profile/personal_access_tokens) +to the config file and save it afterwards. **You need to activate the `api` scope when creating your access token.** + +```yaml +url: https://gitlab.com/api/v4/ +token: 01234567891011 +``` + +## updating + +**Updating from version <= 1.5? Please [click here](https://github.com/kriskbx/gitlab-time-tracker/blob/master/upgrade.md)!** + +Update gtt via yarn: + +```shell +yarn global upgrade gitlab-time-tracker +``` + +## docker + +You don't need to have node and gtt installed on your system in order to use gtt, +you can use the official [Docker image](https://hub.docker.com/r/kriskbx/gitlab-time-tracker) as well: + +```shell +docker run \ + --rm -it \ + -v ~:/root \ + -v $(pwd):/pwd \ + kriskbx/gitlab-time-tracker \ + --help +``` + +`--rm` removes the container after running, `-it` makes it interactive, `-v ~:/root` mounts your home directory to the +home directory inside the container, `-v $(pwd):/pwd` mounts current directory inside the container to gtt be able to read local config. If you want to store the config in another place, mount another directory: + + + ```shell + docker run \ + --rm -it \ + -v /path/to/gtt-config:/root \ + kriskbx/gitlab-time-tracker \ + --help + ``` + +... or use a Docker volume: + +```shell +docker volume create gtt-config + +docker run \ + --rm -it \ + -v gtt-config:/root \ + kriskbx/gitlab-time-tracker \ + --help +``` + +I highly recommend creating an alias and adding it to your `bashrc`: + +```shell +echo "alias gtt='docker run --rm -it -v ~:/root -v $(pwd):/pwd kriskbx/gitlab-time-tracker'" >>~/.bashrc +``` + +Now you can simply write `gtt` instead of the bulky Docker command before. Try it out: `gtt --help` + +**Note:** If you want to save reports via the `--file` parameter, make sure to save them in `/root` or another +mounted directory that you have access to on your host machine. Take a look at the [Docker documentation](https://docs.docker.com/engine/tutorials/dockervolumes/) about how Dopcker handles data and volumes. + +## commands + +### I) configuration + +*[Click here](#configuration) for a complete list of configuration options.* + +**Edit/create the global configuration file, stored in your home directory:** + +```shell +gtt config +``` + +If nothing happens, open the file manually: `~/.gtt/config.yml` - on Windows: `C:\Users\YourUserName\.gtt\config.yml` + +**Edit/create a local configuration file `.gtt.yml` in the current working directory:** + +```shell +gtt config --local +``` + +If a local configuration file is present it will extend of the global one and overwrites global settings. +If you don't want to extend the global configuration file, set the `extend` option in your local config to `false`. +So you can use gtt easily on a per project basis. Make sure to add .gtt.yml to your gitignore file if using a local configuration. + +### II) time tracking + +Time tracking enables you to monitor the time you spent on an issue or merge request locally. +When you're done, you can sync these time records to GitLab: gtt adds the time spent to the given +issue or merge request and automagically keeps everything in sync with your local data. + +Did you forgot to stop time monitoring locally and accidentally synced it to GitLab? +No worries, just edit the local record and run sync again. + +**Start local time monitoring for the given project and issue id:** + +```shell +gtt start "kriskbx/example-project" 15 +gtt start 15 +``` + +If you configured a project in your config you can omit the project. + +**Start local time monitoring for a merge request:** + +```shell +gtt start --type=merge_request "kriskbx/example-project" 15 +gtt start --type=merge_request 15 +``` + +**Start local time monitoring and create a new issue or merge request with the given title:** + +```shell +gtt create "kriskbx/example-project" "New Issue" +gtt create "New Issue" +gtt create --type=merge_request "kriskbx/example-project" "New Issue" +gtt create --type=merge_request "New Issue" +``` + +**Show the current time monitoring status:** + +```shell +gtt status +``` + +**Stop local time monitoring and save as a new time record:** + +```shell +gtt stop +``` + +**Cancel local time monitoring and discard the time record:** + +```shell +gtt cancel +``` + +**Show a list of all local time records (including their ids and meta data):** + +```shell +gtt log +``` +Note: gtt log uses UTC as default timezone. If you want to display the times in a different timezone, make sure to use `timezone: "Europe/Berlin"` in your config. + +**Edit a local time record by the given id:** + +```shell +gtt edit 2XZkV5LNM +gtt edit +``` + +You can omit the id to edit the last added time record. + +**Delete a local time record by the given id:** + +```shell +gtt delete 2XZkV5LNM +gtt delete +``` + +You can omit the id to delete the last added time record. + +**Sync local time records with time tracking data on Gitlab:** + +```shell +gtt sync +gtt sync --proxy="http://localhost:8888" +``` + +You can pass an url to the proxy option if you want to use a proxy server. + +### III) reports + +Get a report for the time tracking data stored on GitLab. If you want to include your local data make sure to sync it +before running the report command. The report command has a lot of options to filter data and output, make sure to +read through the docs before using it. + +#### Get a report + +```shell +gtt report ["namespace/project"] [issue_id] +gtt report "kriskbx/example-project" +gtt report "kriskbx/example-project" 145 +gtt report "kriskbx/example-project" 145 209 45 54 +gtt report +gtt report 145 +gtt report 123 345 123 +``` + +If you configured a project in your config file you can omit it. By passing a or multiple ids you can limit the +report to the given issues or merge requests. *Note: if you're passing a or multiple ids, gtt will fetch issues +with these ids by default. If you want to fetch merge requests you can change the query type using the +`--query` option.* + +#### Query groups and subgroups + +```shell +gtt report ["namespace"] --type=group +gtt report example-group --type=group +gtt report example-group --type=group --subgroups +``` + +Query all projects from the given group and combine the data into a single report. The option `--subgroups` +includes subgroups. + +#### Query multiple projects or groups and combine the data in one report + +```shell +gtt report ["namespace/project"] ["namespace/project"] ... +gtt report "kriskbx/example-project" "kriskbx/example-project-2" +gtt report ["namespace"] ["namespace"] ... --type=group +gtt report example-group example-group-2 --type=group +``` + +*Hint: use the `project_id` or `project_namespace` columns in your report.* + +#### Choose an output for your report + +```shell +gtt report --output=table +gtt report --output=markdown +gtt report --output=csv +gtt report --output=pdf --file=filename.pdf +gtt report --output=xlsx --file=filename.xlsx +``` + +Defaults to `table`. `csv` and `markdown` can be printed to stdout, `pdf` and `xlsx` need the file parameter. + +#### Print the output to a file + +```shell +gtt report --file=filename.txt +``` + +#### Only get time records from the given time frame + +```shell +gtt report --from="2017-03-01" --to="2017-04-01" +``` + +*Note: `--from` defaults to 1970-01-01, `--to` defaults to now. Make sure to use an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) compatible time/date format.* + +There are some quick shorthands: + +```shell +gtt report --today +gtt report --this_week +gtt report --this_month +``` + +#### Include closed issues/merge requests + +```shell +gtt report --closed +``` + +#### Limit to the given user + +```shell +gtt report --user=username +``` + +#### Limit to the given milestone + +```shell +gtt report --milestone=milestone_name +``` + +#### Limit issues and merge requests to the given labels + +```shell +gtt report --include_by_labels=critical --include_by_labels=important +``` + +#### Exclude issues and merge requests that have the given labels + +```shell +gtt report --exclude_by_labels=wont-fix --exclude_by_labels=ignore +``` + +#### Limit the query to the given data type + +```shell +gtt report --query=issues +gtt report --query=merge_requests +``` + +#### Include issues and merge requests in the report that have no time records + +```shell +gtt report --show_without_times +``` + +#### Limit the parts in the final report + +```shell +gtt report --report=stats --report=issues +``` + +*Note: These parts are available: `stats`, `issues`, `merge_requests`, `records`* + +#### Hide headlines in the report + +```shell +gtt report --no_headlines +``` + +#### Hide warnings in the report + +```shell +gtt report --no_warnings +``` + +#### Set date format for the report + +```shell +gtt report --date_format="DD.MM.YYYY HH:mm:ss" +``` + +*Note: [Click here](http://momentjs.com/docs/#/displaying/format/) for a further documentation on the date format.* + +#### Set time format for the report + +```shell +gtt report --time_format="[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" +``` + +*Note: [Click here](#time-format) for a further documentation on the time format.* + +#### Set hours per day + +```shell +gtt report --hours_per_day=6 +``` + +#### Set columns included in the time record table + +```shell +gtt report --record_columns=user --record_columns=date --record_columns=time +``` + +*Note: Available columns to choose from: `user`, `date`, `type`, `iid`, `project_id`, `project_namespace`, `time`* + +#### Set columns included in the issue table + +```shell +gtt report --issue_columns=iid --issue_columns=title --issue_columns=time_username +``` + +*Note: Available columns to choose from: `id`, `iid`, `title`, `project_id`, +`project_namespace`, `description`, `labels`, `milestone`, `assignee`, `author`, +`closed`, `updated_at`, `created_at`, `due_date`, `state`, `spent`, `total_spent`, `total_estimate`* + +*You can also include columns that show the total time spent by a specific user +by following this convention: `time_username`* + +#### Set columns included in the merge request table + +```shell +gtt report --merge_request_columns=iid --merge_request_columns=title --merge_request_columns=time_username +``` + +*Note: Available columns to choose from: `id`, `iid`, `title`, `project_id`, +`project_namespace`, `description`, `labels`, `milestone`, `assignee`, `author`, +`updated_at`, `created_at`, `state`, `spent`, `total_spent`, `total_estimate`* + +*You can also include columns that show the total time spent by a specific user +by following this convention: `time_username`* + +#### Add columns for each project member to issue and merge request table, including their total time spent + +```shell +gtt report --user_columns +``` + +#### Only include the given labels in the report + +```shell +gtt report --include_labels=pending --include_labels=approved +``` + +#### Exclude the given labels from the report + +```shell +gtt report --exclude_labels=bug --exclude_labels=feature +``` + +#### Use a proxy server + +```shell +gtt report --proxy="http://localhost:8080" +``` + +#### Output verbose debug information + +```shell +gtt report --verbose +``` + +## configuration + +The configuration uses the [yaml file format](http://www.yaml.org/spec/1.2/spec.html). +[Click here](#i-configuration) for more information how to edit and create a config file. + +### Config File + +Here's a sample configuration file including all available options: + +```yaml +# Url to the gitlab api +# Make sure there's a trailing slash +# [required] +url: http://gitlab.com/api/v4/ + +# GitLab personal api token +# [required] +token: abcdefghijklmnopqrst + +# Use a proxy server +# defaults to false +proxy: http://localhost:8080 + +# Don't check SSL certificate +# defaults to false +insecure: true + +# Project +# defaults to false +project: namespace/projectname + +# Include closed issues and merge requests +# defaults to false +closed: true + +# Limit to the given milestone +# defaults to false +milestone: milestone_name + +# Exclude issues and merge requests that have the given labels +# defaults to an empty array +excludeByLabels: +- wont-fix +- ignore + +# Limit issues and merge requests to the given labels +# defaults to an empty array +includeByLabels: +- critical +- important + +# Limit the query to the given data type +# available: issues, merge_requests +# defaults to [issues, merge_requests] +query: +- issues + +# Limit the parts in the final report +# available: stats, issues, merge_requests, records +# defaults to [stats, issues, merge_requests, records] +report: +- stats +- records + +# Hide headlines in the report +# defaults to false +noHeadlines: true + +# Hide warnings in the report +# defaults to false +noWarnings: true + +# Include issues and merge requests in the report that have no time records +# defaults to false +showWithoutTimes: true + +# Hours per day +# defaults to 8 +hoursPerDay: 8 + +# Days per week +# defaults to 5 +daysPerWeek: 5 + +# Weeks per month +# defaults to 4 +weeksPerMonth: 4 + +# Include the given columns in the issue table +# See --issue_columns option for more information +# defaults to iid, title, spent, total_estimate +issueColumns: +- iid +- title +- total_estimate + +# Include the given columns in the merge request table +# See --merge_request_columns option for more information +# defaults to iid, title, spent, total_estimate +mergeRequestColumns: +- iid +- title +- total_estimate + +# Include the given columns in the time record table +# See --record_columns option for more information +# defaults to user, date, type, iid, time +recordColumns: +- user +- iid +- time + +# Add columns for each project member to issue and +# merge request table, including their total time spent +# defaults to true +userColumns: true + +# Date format +# Click here for format options: http://momentjs.com/docs/#/displaying/format/ +# defaults to DD.MM.YYYY HH:mm:ss +dateFormat: DD.MM.YYYY HH:mm:ss + +# Time format +# See time format configuration below +# defaults to "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" +timeFormat: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" + +# Time format for different parts of the report +# Instead of specifying one global time format you can specify one for every +# part of the report and the log command +timeFormat: + log: "[%sign][%hours_overall]" + stats: "[%sign][%days_overall]" + issues: "[%sign][%hours_overall]" + merge_requests: "[%sign][%hours_overall]" + records: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" + +# Change your timezone +# default: UTC +timezone: "Europe/Berlin" + +# Output type +# Available: csv, table, markdown, pdf, xlsx +# defaults to table +output: markdown + +# Exclude the given labels from the report +# defaults to an empty array +excludeLabels: +- bug +- feature + +# Only include the given labels in the report +# defaults to an empty array +includeLabels: +- pending +- approved + +# Only works if using a local configuration file! +# Extend the global configuration if set to true, pass a string to extend +# the configuration file stored at the given path +# defaults to true +extend: true + +# Change number of concurrent connections/http queries +# Note: Handle with care, we don't want to spam GitLabs API too much +# defaults to 10 +_parallel: 20 + +# Change rows per page (max. 100) +# defaults to 100 +_perPage: 100 + +# Verbose output +_verbose: false + +# Check access token validity up front +_checkToken: false + +# Skip parsing the issue/merge_request description for time records +_skipDescriptionParsing: false +``` + +### Time format + +##### `[%sign]`, `[%days]`, `[%hours]`, `[%minutes]`, `[%seconds]` + +Prints out the raw data. + +**Example config:** + +```yaml +timeFormat: "[%sign][%days]d [%hours]h [%minutes]m [%seconds]s" +``` + +**Example outputs:** + +```shell +0d 0h 30m 15s +-1d 10h 15m 0s +``` + +##### `[%days>string]`, `[%hours>string]`, `[%minutes>string]`, `[%seconds>string]` + +This is a conditional: it prints out the raw data and appends the given string if the +data is greater than zero. + +**Example config:** + +```yaml +timeFormat: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" +``` + +**Example outputs:** + +```shell +30m 15s +-1d 10h 15m +``` + +##### `[%Days]`, `[%Hours]`, `[%Minutes]`, `[%Seconds]`, `[%Days>string]`, `[%Hours>string]`, `[%Minutes>string]`, `[%Seconds>string]` + +First letter uppercase adds leading zeros. + +**Example config:** + +```yaml +timeFormat: "[%sign][%Days]:[%Hours]:[%Minutes]:[%Seconds]" +``` + +**Example outputs:** + +```shell +00:00:30:15 +-01:10:15:00 +``` + +##### `[%days_overall]`, `[%hours_overall]`, `[%minutes_overall]`, `[%seconds_overall]` + +Instead of printing out the second-, minute-, hour-, day-part of the duration this +prints the complete time in either seconds, minutes, hours or days. + +**Example config:** + +```yaml +timeFormat: "[%sign][%minutes_overall]" +``` + +**Example outputs:** + +```shell +30.25 +1095 +``` + +##### `[%days_overall_comma]`, `[%hours_overall_comma]`, `[%minutes_overall_comma]`, `[%seconds_overall_comma]` + +Use a comma for float values instead of a point. (Useful for some european countries) + +**Example config:** + +```yaml +timeFormat: "[%sign][%minutes_overall]" +``` + +**Example outputs:** + +```shell +30,25 +1095 +``` + +##### `[%hours_overall:2]`, `[%days_overall:3]` + +You can ceil any float value by adding the number of decimals to keep separated with a `:`. + +**Example config:** + +```yaml +timeFormat: "[%sign][%hours_overall:2]" +``` + +**Example outputs:** + +```shell +0,51 +18,25 +``` + +## how to use gtt as a library + +Add as a dependency using yarn: + +```shell +yarn add gitlab-time-tracker +``` + +... or using npm: + +```shell +npm install --save gitlab-time-tracker +``` + +Use it in your project: + +```js +// require modules +const Config = require('gitlab-time-tracker/src/include/config'); +const Report = require('gitlab-time-tracker/src/models/report'); + +// create a default config +let config = new Config(); + +// set required parameters +config.set('token', 'abcdefghijklmnopqrst'); +config.set('project', 'namespace/project'); + +// create report +let report = new Report(config); + +// query and process data +try { + await report.getProject() + await report.getIssues() + await report.getMergeRequests() + await report.processIssues() + await report.processMergeRequests() +} catch (error) { + console.log(error) +} + +// access data on report +report.issues.forEach(issue => { + // time records on issue + console.log(issue.times); + // time spent of single time record + console.log(issue.times[0].time); +}); +report.mergeRequests.forEach(mergeRequest => { + // time records on merge requests + console.log(mergeRequest.times); + // user of single time record + console.log(mergeRequest.times[0].user); +}); +``` + +## dumps + +Starting with 1.7.4 gtt can dump the results of all API requests within a report and use it on another machine without access to the GitLab instance itself. This is very useful for debugging purposes. If you stumble upon a bug which could be unique to your set of data, please rerun the report with these options to save a dump to the given file: `--output=dump --file=/path/dump.json` Check your dump for sensitive information and provide it when asked. + +## faqs + +#### What is the difference between 'total spent' and 'spent'? + +`total spent` is the total amount of time spent in all issues after filtering. +It can include times outside the queried time frame. `spent` on the other hand +is the total amount of time spent in the given time frame. + +## contributing + +I would love to integrate unit testing in this project, but unfortunately my knowledge of +testing in the JavaScript/Node.js world is very limited. (I'm actually a PHP dev) +So this would be a very helpful thing to contribute but of course all contributions are very welcome. + +* Please work in your own branch, e.g. `integrate-awesome-feature`, `fix-awful-bug`, `improve-this-crappy-docs` +* Create a pull request to the `dev` branch + +## support further development + +gtt is an open source project, developed and maintained completely in my free time. + +If you enjoy using gtt you can support the project by [contributing](#contributing) to the code base, +sharing it to your colleagues and co-workers or monetarily by [donating via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DN9YVDKFGC6V6). +Every type of support is helpful and thank you very much if you consider supporting the project +or already have done so. 💜 + +## license + +GPL v2 diff --git a/gtt-sync.js b/gtt-sync.js deleted file mode 100644 index b5bc09c..0000000 --- a/gtt-sync.js +++ /dev/null @@ -1,25 +0,0 @@ -const moment = require('moment'); -const program = require('commander'); - -const Config = require('./include/file-config'); -const Cli = require('./include/cli'); -const Tasks = require('./include/tasks'); - -program - .option('-p --proxy ', 'use a proxy server with the given url') - .option('--verbose', 'show verbose output') - .parse(process.argv); - -Cli.verbose = program.verbose; - -let config = new Config(process.cwd()).set('proxy', program.proxy), - tasks = new Tasks(config); - -tasks.syncResolve() - .then(() => { - if (tasks.sync.frames.length === 0) process.exit(0); - return Cli.bar(`${Cli.process} Syncing time records...`, tasks.sync.frames.length) - }) - .then(() => tasks.syncNotes()) - .then(() => tasks.syncUpdate(Cli.advance)) - .catch(error => Cli.x(error)); \ No newline at end of file diff --git a/models/frame.js b/models/frame.js deleted file mode 100644 index d7c2de6..0000000 --- a/models/frame.js +++ /dev/null @@ -1,92 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const moment = require('moment'); -const Hashids = require('hashids'); -const hashids = new Hashids(); - -class frame { - /** - * constructor. - * @param config - * @param id - * @param type - */ - constructor(config, id, type) { - this.config = config; - this.project = config.get('project'); - this.resource = {id, type}; - this.id = frame.generateId(); - this.start = false; - this.stop = false; - this.notes = []; - } - - static fromJson(config, json) { - let frame = new this(config, json.resource.id, json.resource.type); - frame.project = json.project; - frame.id = json.id; - frame.start = json.start; - frame.stop = json.stop; - frame.notes = json.notes; - - return frame; - } - - static fromFile(config, file) { - return frame.fromJson(config, JSON.parse(fs.readFileSync(file))); - } - - startMe() { - this.start = new Date(); - this.write(); - - return this; - } - - stopMe() { - this.stop = new Date(); - this.write(); - - return this; - } - - /** - * assert file exists - */ - assertFile() { - if (!fs.existsSync(this.file)) fs.appendFileSync(this.file, ''); - } - - /** - * write data to file - */ - write() { - if (fs.existsSync(this.file)) fs.unlinkSync(this.file); - fs.appendFileSync(this.file, JSON.stringify({ - id: this.id, - project: this.project, - resource: this.resource, - notes: this.notes, - start: this.start, - stop: this.stop - }, null, "\t")); - } - - get file() { - return path.join(this.config.frameDir, this.id + '.json'); - } - - get duration() { - return moment(this.stop).diff(this.start) / 1000; - } - - /** - * generate a unique id - * @returns {number} - */ - static generateId() { - return hashids.encode(new Date().getTime()); - } -} - -module.exports = frame; \ No newline at end of file diff --git a/models/frameCollection.js b/models/frameCollection.js deleted file mode 100644 index e01df10..0000000 --- a/models/frameCollection.js +++ /dev/null @@ -1,43 +0,0 @@ -const Base = require('./base'); -const Frame = require('./frame'); -const Fs = require('./../include/filesystem'); - -class frameCollection extends Base { - constructor(config) { - super(config); - - this.frames = Fs.readDir(config.frameDir); - } - - filter(func) { - let arr = []; - - this.frames.forEach(file => { - let frame = Frame.fromFile(this.config, Fs.join(this.config.frameDir, file)); - if (frame.stop === false) { - return; - } - - if (func(frame)) { - arr.push(file); - } - }); - - this.frames = arr; - } - - forEach(iterator) { - return this.parallel(this.frames, (file, done) => { - let frame = Frame.fromFile(this.config, Fs.join(this.config.frameDir, file)); - if (frame.stop === false) return done(); - - iterator(frame, done); - }); - } - - get length() { - return this.frames.length; - } -} - -module.exports = frameCollection; \ No newline at end of file diff --git a/models/project.js b/models/project.js deleted file mode 100644 index bbac3ae..0000000 --- a/models/project.js +++ /dev/null @@ -1,55 +0,0 @@ -const Base = require('./base'); - -/** - * project model - */ -class project extends Base { - /** - * construct - * @param config - * @param data - */ - constructor(config, data) { - super(config); - this.data = data; - } - - /** - * make - * @param name - */ - make(name) { - let promise = this.get(`projects/${encodeURIComponent(name)}`); - promise.then(project => this.data = project.body); - - return promise; - } - - /** - * set members - * @returns {Promise} - */ - members() { - let promise = this.get(`projects/${this.id}/members`); - promise.then(response => this.members = response.body); - - return promise; - } - - /* - * properties - */ - get id() { - return this.data.id; - } - - get name() { - return this.data.path_with_namespace; - } - - get users() { - return this.members.map(member => member.username); - } -} - -module.exports = project; \ No newline at end of file diff --git a/package.json b/package.json old mode 100644 new mode 100755 index a637ec0..9496a9c --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitlab-time-tracker", - "version": "1.5.5", + "version": "1.7.39", "description": "A command line interface for GitLabs time tracking feature.", "bugs": { "url": "https://github.com/kriskbx/gitlab-time-tracker/issues" @@ -9,34 +9,66 @@ "type": "git", "url": "https://github.com/kriskbx/gitlab-time-tracker.git" }, - "main": "gtt.js", - "scripts": {}, + "main": "src/gtt.js", + "scripts": { + "test": "NODE_ENV=test mocha 'spec/**/*.spec.js'", + "cover": "istanbul cover _mocha $(find ./spec -name \"*.spec.js\")", + "coveralls": "yarn run cover -- --report lcovonly && cat ./coverage/lcov.info | coveralls", + "build": "pkg src/gtt.js -o out/gtt -c package.json" + }, "bin": { - "gtt": "gtt.js" + "gtt": "src/gtt.js" + }, + "engines": { + "node": ">=6.11" + }, + "pkg": { + "scripts": "src/**/*.js", + "targets": [ + "node10-linux-x64", + "node10-macos-x64", + "node10-win-x64" + ] }, "author": "kriskbx", "license": "GPL-2.0", "dependencies": { - "async": "^2.4.0", + "app-module-path": "^2.2.0", + "async": "^2.6.1", "camelcase": "^4.1.0", "cli-cursor": "^2.1.0", "cli-table": "^0.3.1", - "colors": "^1.1.2", - "commander": "^2.9.0", + "colors": "^1.3.1", + "commander": "kriskbx/commander.js", "csv-string": "^2.3.2", "find-in-files": "^0.4.0", "hash-sum": "^1.0.2", "hashids": "^1.1.1", - "markdown-pdf": "^8.0.0", + "markdown-pdf": "^9.0.0", "markdown-table": "^1.1.0", - "moment": "^2.18.1", + "moment": "^2.22.2", + "moment-timezone": "^0.5.21", "node-spinner": "^0.0.4", "open": "^0.0.5", "progress": "^2.0.0", "prompt": "^1.0.0", "read-yaml": "^1.1.0", - "request": "^2.81.0", + "request": "^2.87.0", "request-promise-native": "^1.0.4", - "underscore": "^1.8.3" + "shelljs": "^0.8.3", + "tempfile": "^2.0.0", + "throttled-queue": "^1.0.7", + "underscore": "^1.9.1", + "xdg-basedir": "^3.0.0", + "xlsx": "^0.13.5" + }, + "devDependencies": { + "chai": "^4.1.2", + "coveralls": "^2.13.1", + "istanbul": "^0.4.5", + "mocha": "^5", + "mocha-lcov-reporter": "^1.3.0", + "pkg": "^4.3.4", + "sinon": "^3.2.1" } } diff --git a/preview/demo.gif b/preview/demo.gif new file mode 100644 index 0000000..6904fa1 Binary files /dev/null and b/preview/demo.gif differ diff --git a/preview/icon.gvdesign b/preview/icon.gvdesign new file mode 100644 index 0000000..56a1d79 Binary files /dev/null and b/preview/icon.gvdesign differ diff --git a/preview/icon.png b/preview/icon.png new file mode 100755 index 0000000..d4bfe03 Binary files /dev/null and b/preview/icon.png differ diff --git a/readme.md b/readme.md old mode 100644 new mode 100755 index 243f38b..bc99b29 --- a/readme.md +++ b/readme.md @@ -1,757 +1,31 @@ -# gtt +![gtt](https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker/master/preview/icon.png) -[![npm](https://img.shields.io/npm/dt/gitlab-time-tracker.svg?style=flat-square)](https://www.npmjs.com/package/gitlab-time-tracker) [![npm](https://img.shields.io/npm/v/gitlab-time-tracker.svg?style=flat-square)](https://www.npmjs.com/package/gitlab-time-tracker) [![npm](https://img.shields.io/npm/l/gitlab-time-tracker.svg)](https://www.npmjs.com/package/gitlab-time-tracker) +

+ + + + + +

-> A command line interface for GitLabs time tracking feature. +## ⚠️ Attention -gtt monitors the time you spent on an issue or merge request locally and syncs it to GitLab. -It also allows you to create reports in various formats from time tracking data -stored on GitLab. +This repository is not maintained anymore. If you still want to use gtt and get regular updates, I recommend checking out this fork: https://github.com/ndu2/gitlab-time-tracker -## contents +## introduction -* [requirements](#requirements) -* [installation](#installation) -* [updating](#updating) -* [docker](#docker) -* [commands](#commands) - * [I) configuration](#i-configuration) - * [II) time tracking](#ii-time-tracking) - * [III) reports](#iii-reports) -* [configuration](#configuration) - * [config file](#config-file) - * [time format](#time-format) -* [how to use gtt as a library](#how-to-use-gtt-as-a-library) -* [faqs](#faqs) -* [contributing](#contributing) -* [buy me a beer 🍺](#buy-me-a-beer) -* [license](#license) +gtt is a fully featured command line interface for GitLab's time tracking feature. It monitors the time you spent on an issue or merge request locally and syncs it to GitLab. It also allows you to create reports in various formats from time tracking data stored on GitLab. **Looking for a graphical user interface? Check out the [gtt taskbar/menubar app](https://github.com/kriskbx/gitlab-time-tracker-taskbar) for Linux, Mac & Windows!** -## requirements +![gtt demo](https://raw.githubusercontent.com/kriskbx/gitlab-time-tracker/master/preview/demo.gif) -* [node.js](https://nodejs.org/en/download) version >= 6 -* [npm](https://github.com/npm/npm) or [yarn](https://yarnpkg.com/en/docs/install) +## documentation -## installation +How to install and use gtt? You can find the documentation [here](https://github.com/kriskbx/gitlab-time-tracker/blob/master/documentation.md). -Install the gtt command line interface using yarn: +## support further development -```shell -yarn global add gitlab-time-tracker --prefix /usr/local -``` - -... or using npm: - -```shell -npm install -g gitlab-time-tracker -``` - -Run the config command to create a config file and open it in your default editor. -If nothing happens, open the file manually: `~/.gtt/config.yml` - -```shell -gtt config -``` - -Add your GitLab API url and your [GitLab API personal token](https://gitlab.com/profile/personal_access_tokens) -to the config file and save it afterwards: - -```yaml -url: https://gitlab.com/api/v4/ -token: 01234567891011 -``` - -## updating - -Update gtt via yarn: - -```shell -yarn global upgrade gitlab-time-tracker -``` - -... or npm: - -```shell -npm install -g gitlab-time-tracker -``` - -## docker - -You don't need to have node and gtt installed on your system in order to use gtt, -you can use the official [Docker image](https://hub.docker.com/r/kriskbx/gitlab-time-tracker) as well: - -```shell -docker run \ - --rm -it \ - -v ~:/root \ - kriskbx/gitlab-time-tracker \ - --help -``` - -`--rm` removes the container after running, `-it` makes it interactive, `-v ~:/root` mounts your home directory to the -home directory inside the container. If you want to store the config in another place, mount another directory: - - - ```shell - docker run \ - --rm -it \ - -v /path/to/gtt-config:/root \ - kriskbx/gitlab-time-tracker \ - --help - ``` - -... or use a Docker volume: - -```shell -docker volume create gtt-config - -docker run \ - --rm -it \ - -v gtt-config:/root \ - kriskbx/gitlab-time-tracker \ - --help -``` - -I highly recommend creating an alias and adding it to your `bashrc`: - -```shell -echo "alias gtt='docker run --rm -it -v ~:/root kriskbx/gitlab-time-tracker'" >>~/.bash_rc -``` - -Now you can simply write `gtt` instead of the bulky Docker command before. Try it out: `gtt --help` - -**Note:** If you want to save reports via the `--file` parameter, make sure to save them in `/root` or another -mounted directory that you have access to on your host machine. Take a look at the [Docker documentation](https://docs.docker.com/engine/tutorials/dockervolumes/) about how Docker handles data and volumes. - -## commands - -### I) configuration - -*[Click here](#configuration) for a complete list of configuration options.* - -**Edit/create the global configuration file, stored in your home directory:** - -```shell -gtt config -``` - -**Edit/create a local configuration file `.gtt.yml` in the current working directory:** - -```shell -gtt config --local -``` - -If a local configuration file is present it's used instead of the global one. -So you can use gtt on a per project basis. Make sure to add .gtt.yml to your -gitignore file if using a local configuration. - -### II) time tracking - -Time tracking enables you to monitor the time you spent on an issue or merge request locally. -When you're done, you can sync these time records to GitLab: gtt adds the time spent to the given -issue or merge request and automagically keeps everything in sync with your local data. - -Did you forgot to stop time monitoring locally and accidentally synced it to GitLab? -No worries, just edit the local record and run sync again. - -**Start local time monitoring for the given project and issue id:** - -```shell -gtt start "kriskbx/example-project" 15 -gtt start 15 -``` - -If you configured a project in your config you can omit the project. - -**Start local time monitoring for a merge request:** - -```shell -gtt start --type=merge_request "kriskbx/example-project" 15 -gtt start --type=merge_request 15 -``` - -If you configured a project in your config you can omit the project. - -**Show the current time monitoring status:** - -```shell -gtt status -``` - -**Stop local time monitoring and save as a new time record:** - -```shell -gtt stop -``` - -**Cancel local time monitoring and discard the time record:** - -```shell -gtt cancel -``` - -**Show a list of all local time records (including their ids and meta data):** - -```shell -gtt log -``` - -**Edit a local time record by the given id:** - -```shell -gtt edit 2XZkV5LNM -gtt edit -``` - -You can omit the id to edit the last added time record. - -**Delete a local time record by the given id:** - -```shell -gtt delete 2XZkV5LNM -gtt delete -``` - -You can omit the id to delete the last added time record. - -**Sync local time records with time tracking data on Gitlab:** - -```shell -gtt sync -gtt sync --proxy="http://localhost:8888" -``` - -You can pass an url to the proxy option if you want to use a proxy server. - -### III) reports - -Get a report for the time tracking data stored on GitLab. If you want to include your local data make sure to sync it -before running the report command. The report command has a lot of options to filter data and output, make sure to -read through the docs before using it. - -#### Get a report - -```shell -gtt report ["namespace/project"] [issue_id] -gtt report "kriskbx/example-project" -gtt report "kriskbx/example-project" 145 -gtt report "kriskbx/example-project" 145 209 45 54 -gtt report -gtt report 145 -gtt report 123 345 123 -``` - -If you configured a project in your config file you can omit it. By passing a or multiple ids you can limit the -report to the given issues or merge requests. *Note: if you're passing a or multiple ids, gtt will fetch issues -with these ids by default. If you want to fetch merge requests you can change the query type using the -`--query` option.* - -#### Query groups and subgroups - -```shell -gtt report ["namespace"] --type=group -gtt report example-group --type=group -gtt report example-group --type=group --subgroups -``` - -Query all projects from the given group and combine the data into a single report. The option `--subgroups` -includes subgroups. - -#### Query multiple projects or groups and combine the data in one report - -```shell -gtt report ["namespace/project"] ["namespace/project"] ... -gtt report "kriskbx/example-project" "kriskbx/example-project-2" -gtt report ["namespace"] ["namespace"] ... --type=group -gtt report example-group example-group-2 --type=group -``` - -*Hint: use the `project_id` or `project_namespace` columns in your report.* - -#### Choose an output for your report - -```shell -gtt report --output=table -gtt report --output=markdown -gtt report --output=csv -gtt report --output=pdf --file=filename.pdf -``` - -Defaults to `table`. `csv` and `markdown` can be printed to stdout, `pdf` needs the file parameter. - -#### Print the output to a file - -```shell -gtt report --file=filename.txt -``` - -#### Only get time records from the given time frame - -```shell -gtt report --from="2017-03-01" --to="2017-04-01" -``` - -*Note: `--from` defaults to 1970-01-01, `--to` defaults to now. Make sure to use an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) compatible time/date format.* - -#### Include closed issues/merge requests - -```shell -gtt report --closed -``` - -#### Limit to the given user - -```shell -gtt report --user=username -``` - -#### Limit to the given milestone - -```shell -gtt report --milestone=milestone_name -``` - -#### Limit issues and merge requests to the given labels - -```shell -gtt report --include_by_labels=critical --include_by_labels=important -``` - -#### Exclude issues and merge requests that have the given labels - -```shell -gtt report --exclude_by_labels=wont-fix --exclude_by_labels=ignore -``` - -#### Limit the query to the given data type - -```shell -gtt report --query=issues -gtt report --query=merge_requests -``` - -#### Include issues and merge requests in the report that have no time records - -```shell -gtt report --show_without_times -``` - -#### Limit the parts in the final report - -```shell -gtt report --report=stats --report=issues -``` - -*Note: These parts are available: `stats`, `issues`, `merge_requests`, `records`* - -#### Hide headlines in the report - -```shell -gtt report --no_headlines -``` - -#### Hide warnings in the report - -```shell -gtt report --no_warnings -``` - -#### Set date format for the report - -```shell -gtt report --date_format="DD.MM.YYYY HH:mm:ss" -``` - -*Note: [Click here](http://momentjs.com/docs/#/displaying/format/) for a further documentation on the date format.* - -#### Set time format for the report - -```shell -gtt report --time_format="[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" -``` - -*Note: [Click here](#time-format) for a further documentation on the time format.* - -#### Set columns included in the time record table - -```shell -gtt report --record_columns=user --record_columns=date --record_columns=time -``` - -*Note: Available columns to choose from: `user`, `date`, `type`, `iid`, `project_id`, `project_namespace`, `time`* - -#### Set columns included in the issue table - -```shell -gtt report --issue_columns=iid --issue_columns=title --issue_columns=time_username -``` - -*Note: Available columns to choose from: `id`, `iid`, `title`, `project_id`, -`project_namespace`, `description`, `labels`, `milestone`, `assignee`, `author`, -`closed`, `updated_at`, `created_at`, `state`, `spent`, `total_spent`, `total_estimate`* - -*You can also include columns that show the total time spent by a specific user -by following this convention: `time_username`* - -#### Set columns included in the merge request table - -```shell -gtt report --merge_request_columns=iid --merge_request_columns=title --merge_request_columns=time_username -``` - -*Note: Available columns to choose from: `id`, `iid`, `title`, `project_id`, -`project_namespace`, `description`, `labels`, `milestone`, `assignee`, `author`, -`updated_at`, `created_at`, `state`, `spent`, `total_spent`, `total_estimate`* - -*You can also include columns that show the total time spent by a specific user -by following this convention: `time_username`* - -#### Add columns for each project member to issue and merge request table, including their total time spent - -```shell -gtt report --user_columns -``` - -#### Only include the given labels in the report - -```shell -gtt report --include_labels=pending --include_labels=approved -``` - -#### Exclude the given labels from the report - -```shell -gtt report --exclude_labels=bug --exclude_labels=feature -``` - -#### Use a proxy server - -```shell -gtt report --proxy="http://localhost:8080" -``` - -#### Output verbose debug information - -```shell -gtt report --verbose -``` - -## configuration - -The configuration uses the [yaml file format](http://www.yaml.org/spec/1.2/spec.html). -[Click here](#i-configuration) for more information how to edit and create a config file. - -### Config File - -Here's a sample configuration file including all available options: - -```yaml -# Url to the gitlab api -# Make sure there's a trailing slash -# [required] -url: http://gitlab.com/api/v4/ - -# GitLab personal api token -# [required] -token: abcdefghijklmnopqrst - -# Use a proxy server -# defaults to false -proxy: http://localhost:8080 - -# Project -# defaults to false -project: namespace/projectname - -# Include closed issues and merge requests -# defaults to false -closed: true - -# Limit to the given milestone -# defaults to false -milestone: milestone_name - -# Exclude issues and merge requests that have the given labels -# defaults to an empty array -excludeByLabels: -- wont-fix -- ignore - -# Limit issues and merge requests to the given labels -# defaults to an empty array -includeByLabels: -- critical -- important - -# Limit the query to the given data type -# available: issues, merge_requests -# defaults to [issues, merge_requests] -query: -- issues - -# Limit the parts in the final report -# available: stats, issues, merge_requests, records -# defaults to [stats, issues, merge_requests, records] -report: -- stats -- records - -# Hide headlines in the report -# defaults to false -noHeadlines: true - -# Hide warnings in the report -# defaults to false -noWarnings: true - -# Include issues and merge requests in the report that have no time records -# defaults to false -showWithoutTimes: true - -# Hours per day -# defaults to 8 -hoursPerDay: 8 - -# Include the given columns in the issue table -# See --issue_columns option for more information -# defaults to iid, title, spent, total_estimate -issueColumns: -- iid -- title -- estimation - -# Include the given columns in the merge request table -# See --merge_request_columns option for more information -# defaults to iid, title, spent, total_estimate -mergeRequestColumns: -- iid -- title -- estimation - -# Include the given columns in the time record table -# See --record_columns option for more information -# defaults to user, date, type, iid, time -recordColumns: -- user -- iid -- time - -# Add columns for each project member to issue and -# merge request table, including their total time spent -# defaults to true -userColumns: true - -# Date format -# Click here for format options: http://momentjs.com/docs/#/displaying/format/ -# defaults to DD.MM.YYYY HH:mm:ss -dateFormat: DD.MM.YYYY HH:mm:ss - -# Time format -# See time format configuration below -# defaults to "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" -timeFormat: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" - -# Output type -# Available: csv, table, markdown, pdf -# defaults to table -output: markdown - -# Exclude the given labels from the report -# defaults to an empty array -excludeLabels: -- bug -- feature - -# Only include the given labels in the report -# defaults to an empty array -includeLabels: -- pending -- approved - -# Change number of concurrent connections/http queries -# Note: Handle with care, we don't want to spam GitLabs API too much -# defaults to 10 -_parallel: 20 - -# Change rows per page (max. 100) -# defaults to 100 -_perPage: 100 -``` - -### Time format - -##### `[%sign]`, `[%days]`, `[%hours]`, `[%minutes]`, `[%seconds]` - -Prints out the raw data. - -**Example config:** - -```yaml -timeFormat: "[%sign][%days]d [%hours]h [%minutes]m [%seconds]s" -``` - -**Example outputs:** - -```shell -0d 0h 30m 15s --1d 10h 15m 0s -``` - -##### `[%days>string]`, `[%hours>string]`, `[%minutes>string]`, `[%seconds>string]` - -This is a conditional: it prints out the raw data and appends the given string if the -data is greater than zero. - -**Example config:** - -```yaml -timeFormat: "[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]" -``` - -**Example outputs:** - -```shell -30m 15s --1d 10h 15m -``` - -##### `[%Days]`, `[%Hours]`, `[%Minutes]`, `[%Seconds]`, `[%Days>string]`, `[%Hours>string]`, `[%Minutes>string]`, `[%Seconds>string]` - -First letter uppercase adds leading zeros. - -**Example config:** - -```yaml -timeFormat: "[%sign][%Days]:[%Hours]:[%Minutes]:[%Seconds]" -``` - -**Example outputs:** - -```shell -00:00:30:15 --01:10:15:00 -``` - -##### `[%days_overall]`, `[%hours_overall]`, `[%minutes_overall]`, `[%seconds_overall]` - -Instead of printing out the second-, minute-, hour-, day-part of the duration this -prints the complete time in either seconds, minutes, hours or days. - -**Example config:** - -```yaml -timeFormat: "[%sign][%minutes_overall]" -``` - -**Example outputs:** - -```shell -30.25 -1095 -``` - -##### `[%days_overall_comma]`, `[%hours_overall_comma]`, `[%minutes_overall_comma]`, `[%seconds_overall_comma]` - -Use a comma for float values instead of a point. (Useful for some european countries) - -**Example config:** - -```yaml -timeFormat: "[%sign][%minutes_overall]" -``` - -**Example outputs:** - -```shell -30,25 -1095 -``` - -## how to use gtt as a library - -Add as a dependency using yarn: - -```shell -yarn add gitlab-time-tracker -``` - -... or using npm: - -```shell -npm install --save gitlab-time-tracker -``` - -Use it in your project: - -```js -// require modules -const Config = require('gitlab-time-tracker/include/config'); -const Report = require('gitlab-time-tracker/models/report'); - -// create a default config -let config = new Config(); - -// set some vars on config -config.set('token', 'abcdefghijklmnopqrst'); -config.set('project', 'namespace/project'); - -// create report -let report = new Report(config); - -// query and process data -try { - await report.getProject() - await report.getIssues() - await report.getMergeRequests() - await report.processIssues() - await report.processMergeRequests() -} catch (error) { - console.log(error) -} - -// access data on report -report.issues.forEach(issue => { - console.log(issue.times); - console.log(issue.times[0].time); -}); -report.mergeRequests.forEach(mergeRequest => { - console.log(mergeRequests.times); - console.log(issue.times[0].user); -}); -``` - -## faqs - -#### What is the difference between 'total spent' and 'spent'? - -`total spent` is the total amount of time spent in all issues after filtering. -It can include times outside the queried time frame. `spent` on the other hand -is the total amount of time spent in the given time frame. - -#### Why 'total spent' and 'spent' are showing different amounts. - -gtt can only track time records from notes/comments. If you start your -issue or merge request with `/spent [time]` in its description, gtt won't -take it into consideration (for now). - -## contributing - -I would love to integrate unit testing in this project, but unfortunately my knowledge of -testing in the JavaScript/Node.js world is very limited. (I'm actually a PHP dev) -So this would be a very helpful thing to contribute but of course all contributions are very welcome. - -* Please work in your own branch, e.g. `integrate-awesome-feature`, `fix-awful-bug`, `improve-this-crappy-docs` -* Create a pull request to the `dev` branch - -## buy me a beer - -gtt is an open source project, developed and maintained completely in my free time. - -If you enjoy using gtt you can support the project by [contributing](#contributing) to the code base, -sharing it to your colleagues and co-workers or monetarily by [donating via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DN9YVDKFGC6V6). -Every type of support is helpful and thank you very much if you consider supporting the project -or already have done so. 💜 +Please support the development of this free software by [donating or sharing](https://github.com/kriskbx/gitlab-time-tracker/blob/master/documentation.md#support-further-development)! ## license -GPL v2 \ No newline at end of file +gtt is open-source software licensed under the [GPL V2 license](https://github.com/kriskbx/gitlab-time-tracker/blob/master/LICENSE). diff --git a/spec/_setup.spec.js b/spec/_setup.spec.js new file mode 100755 index 0000000..e676c17 --- /dev/null +++ b/spec/_setup.spec.js @@ -0,0 +1,10 @@ +const sinon = require('sinon'); +const chai = require('chai'); + +beforeEach(function () { + this.sandbox = sinon.sandbox.create() +}); + +afterEach(function () { + this.sandbox.restore() +}); \ No newline at end of file diff --git a/spec/include/config.spec.js b/spec/include/config.spec.js new file mode 100755 index 0000000..41cf003 --- /dev/null +++ b/spec/include/config.spec.js @@ -0,0 +1,77 @@ +const config = require('./../../src/include/config'); +const expect = require('chai').expect; + +describe('The config class', () => { + it('stores data', () => { + let Config = new config(), + data = 'value_' + Math.random(), + key = 'key_' + Math.random(); + + Config.set(key, data); + + expect(Config.get(key)).to.equal(data); + }); + + it('doesnt store data if the value is null or undefined until you force it', () => { + let Config = new config(), + data = 'value_' + Math.random(), + key = 'key_' + Math.random(); + + Config.set(key, data); + + Config.set(key, null); + expect(Config.get(key)).to.equal(data); + + Config.set(key, undefined); + expect(Config.get(key)).to.equal(data); + + Config.set(key, null, true); + expect(Config.get(key)).to.equal(null); + + Config.set(key, undefined, true); + expect(Config.get(key)).to.equal(undefined); + }); + + it('returns moment instances for dates', () => { + let Config = new config(), + dates = ['from', 'to']; + + dates.forEach(date => { + Config.set(date, "2017-08-01"); + expect(typeof Config.get(date).format).to.equal("function"); + }); + }); + + it('returns values from objects and falls back to the default', () => { + let Config = new config(), + objectsWithDefaults = ['timeFormat'], + defaults = Object.assign({}, Config.data), + stringData = 'booze', + objectData = { + foo: 'bar', + baz: 'bar' + }; + + objectsWithDefaults.forEach(key => { + Config.set(key, objectData); + + expect(Config.get(key, 'foo')).to.equal('bar'); + expect(Config.get(key, 'baz')).to.equal('bar'); + expect(Config.get(key, 'not_a_real_key')).to.equal(defaults[key]); + + Config.set(key, stringData); + + expect(Config.get(key, 'foo')).to.equal('booze'); + expect(Config.get(key, 'baz')).to.equal('booze'); + expect(Config.get(key, 'not_a_real_key')).to.equal('booze'); + }); + }); + + it('makes durations human readable', () => { + let Config = new config(), + humanReadable = "1d 4h 30m 10s", + seconds = 45010; + + expect(Config.toHumanReadable(seconds)).to.equal(humanReadable); + }); +}); \ No newline at end of file diff --git a/spec/include/file-config.spec.js b/spec/include/file-config.spec.js new file mode 100755 index 0000000..96f919f --- /dev/null +++ b/spec/include/file-config.spec.js @@ -0,0 +1,3 @@ +describe('The file config class', () => { + +}); \ No newline at end of file diff --git a/gtt-cancel.js b/src/gtt-cancel.js old mode 100644 new mode 100755 similarity index 53% rename from gtt-cancel.js rename to src/gtt-cancel.js index c7b1b43..738471a --- a/gtt-cancel.js +++ b/src/gtt-cancel.js @@ -17,6 +17,11 @@ let tasks = new Tasks(config); tasks.cancel() .then(frames => { - frames.forEach(frame => console.log(`Cancel project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green}`)) + frames.forEach(frame => { + if(!frame.resource.new) + return console.log(`Cancel project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green}`) + + console.log(`Cancel project ${frame.project.magenta} for new ${frame.resource.type} "${(frame.resource.id).blue}", started ${moment(frame.start).fromNow().green}`) + }) }) .catch(error => Cli.error(error)); \ No newline at end of file diff --git a/gtt-config.js b/src/gtt-config.js old mode 100644 new mode 100755 similarity index 100% rename from gtt-config.js rename to src/gtt-config.js diff --git a/src/gtt-create.js b/src/gtt-create.js new file mode 100755 index 0000000..20adec0 --- /dev/null +++ b/src/gtt-create.js @@ -0,0 +1,31 @@ +const colors = require('colors'); +const moment = require('moment'); +const program = require('commander'); + +const Config = require('./include/file-config'); +const Cli = require('./include/cli'); +const Tasks = require('./include/tasks'); + +program + .arguments('[project] [title]') + .option('-t, --type ', 'specify resource type: issue, merge_request') + .option('--verbose', 'show verbose output') + .parse(process.argv); + +Cli.verbose = program.verbose; + +let config = new Config(process.cwd()), + tasks = new Tasks(config), + type = program.type ? program.type : 'issue', + title = program.args.length === 1 ? program.args[0] : program.args[1], + project = program.args.length === 2 ? program.args[0] : null; + +if (program.args.length < 2 && !config.get('project')) + Cli.error('No project set'); + +if (!title) + Cli.error('Wrong or missing title'); + +tasks.start(project, type, title) + .then(frame => console.log(`Starting project ${config.get('project').magenta} and create ${type} "${title.blue}" at ${moment().format('HH:mm').green}`)) + .catch(error => Cli.error(error)); \ No newline at end of file diff --git a/gtt-delete.js b/src/gtt-delete.js old mode 100644 new mode 100755 similarity index 100% rename from gtt-delete.js rename to src/gtt-delete.js diff --git a/gtt-edit.js b/src/gtt-edit.js old mode 100644 new mode 100755 similarity index 100% rename from gtt-edit.js rename to src/gtt-edit.js diff --git a/gtt-log.js b/src/gtt-log.js old mode 100644 new mode 100755 similarity index 51% rename from gtt-log.js rename to src/gtt-log.js index 7ef4ee0..7dd85d8 --- a/gtt-log.js +++ b/src/gtt-log.js @@ -1,23 +1,27 @@ const _ = require('underscore'); const program = require('commander'); const colors = require('colors'); -const moment = require('moment'); +const moment = require('moment-timezone'); const Config = require('./include/file-config'); const Cli = require('./include/cli'); const Time = require('./models/time'); const Tasks = require('./include/tasks'); -program.option('--verbose', 'show verbose output') +program + .option('--verbose', 'show verbose output') + .option('--hours_per_day ', 'hours per day for human readable time formats') + .option('--time_format ', 'time format') .parse(process.argv); Cli.verbose = program.verbose; -let config = new Config(__dirname), - tasks = new Tasks(config); +let config = new Config(__dirname).set('hoursPerDay', program.hours_per_day), + tasks = new Tasks(config), + timeFormat = config.set('timeFormat', program.time_format).get('timeFormat', 'log'); function toHumanReadable(input) { - return Time.toHumanReadable(Math.ceil(input), config.get('hoursPerDay'), config.get('timeFormat')); + return Time.toHumanReadable(Math.ceil(input), config.get('hoursPerDay'), timeFormat); } tasks.log() @@ -27,9 +31,10 @@ tasks.log() console.log(`${moment(date).format('MMMM Do YYYY')} (${toHumanReadable(times[date])})`.green); frames[date] - .sort((a, b) => moment(a.start).isBefore(moment(b.start)) ? -1 : 1) + .sort((a, b) => a.start.isBefore(b.start) ? -1 : 1) .forEach(frame => { - console.log(` ${frame.id} ${moment(frame.start).format('HH:mm').green} to ${moment(frame.stop).format('HH:mm').green}\t${toHumanReadable(frame.duration)}\t\t${frame.project.magenta}\t\t${(frame.resource.type + ' #' + frame.resource.id).blue}`) + let issue = frame.resource.new ? `new ${frame.resource.type + ' "' + frame.resource.id.blue}"` : `${(frame.resource.type + ' #' + frame.resource.id).blue}`; + console.log(` ${frame.id} ${frame.start.clone().format('HH:mm').green} to ${frame.stop.clone().format('HH:mm').green}\t${toHumanReadable(frame.duration)}\t\t${frame.project.magenta}\t\t${issue}`) }); }); } diff --git a/gtt-report.js b/src/gtt-report.js old mode 100644 new mode 100755 similarity index 81% rename from gtt-report.js rename to src/gtt-report.js index 5c311a5..fac46da --- a/gtt-report.js +++ b/src/gtt-report.js @@ -13,7 +13,9 @@ const Output = { table: require('./output/table'), csv: require('./output/csv'), pdf: require('./output/pdf'), - markdown: require('./output/markdown') + markdown: require('./output/markdown'), + dump: require('./output/dump'), + xlsx: require('./output/xlsx') }; // this collects options @@ -27,17 +29,25 @@ function collect(val, arr) { // set options program .arguments('[project] [ids]') + .option('-e --type ', 'specify the query type: project, user, group') + .option('--subgroups', 'include sub groups') + .option('--url ', 'URL to GitLabs API') + .option('--token ', 'API access token') + .option('-p --proxy ', 'use a proxy server with the given url') + .option('--insecure', 'don\'t check certificates') .option('-f --from ', 'query times that are equal or greater than the given date') .option('-t --to ', 'query times that are equal or smaller than the given date') + .option('--today', 'ignores --from and --to and queries entries for today') + .option('--this_week', 'ignores --from and --to and queries entries for this week') + .option('--this_month', 'ignores --from and --to and queries entries for this month') .option('-c --closed', 'include closed issues') - .option('-u --user ', 'only query times from the given user') .option('-m --milestone ', 'include issues from the given milestone') + .option('--hours_per_day ', 'hours per day for human readable time formats') + .option('-u --user ', 'only query times from the given user') .option('-q --query ', 'query the given data types: issues, merge_requests', collect, null) - .option('-e --type ', 'specify the query type: project, user, group') .option('-r --report ', 'include in the report: stats, issues, merge_requests, records', collect, null) - .option('-o --output ', 'use the given output', collect, null) + .option('-o --output ', 'use the given output') .option('-l --file ', 'save report to the given file') - .option('--subgroups', 'include sub groups') .option('--include_by_labels ', 'only include issues that have the given labels', collect, null) .option('--exclude_by_labels ', 'exclude issues that have the given labels', collect, null) .option('--include_labels ', 'only include the given labels in the report', collect, null) @@ -53,15 +63,36 @@ program .option('--quiet', 'only output report') .option('--verbose', 'show verbose output') .option('--show_without_times', 'show issues/merge requests without time records') - .option('-p --proxy ', 'use a proxy server with the given url') + .option('--from_dump ', 'instead of querying gitlab, use data from the given dump file') .parse(process.argv); // init helpers let config = new Config(process.cwd()); let cli = new Cli(program.args); +// if using a dump, set the config accordingly +if (program.from_dump && fs.existsSync(program.from_dump)) { + let data = JSON.parse(fs.readFileSync(program.from_dump)); + + if (data.data) _.each(data.data, (v, i) => { + config.set(i, v); + }); + + if (data._dump) config.dump = data._dump; +} + +// if writing a dump, set config accordingly +if (program.output === "dump") { + config.on("dump-updated", () => { + new Output['dump'](config); + }); +} + // overwrite config with args and opts config + .set('url', program.url) + .set('token', program.token) + .set('insecure', program.insecure) .set('project', cli.project()) .set('iids', cli.iids()) .set('from', program.from) @@ -75,6 +106,7 @@ config .set('excludeLabels', program.exclude_labels) .set('dateFormat', program.date_format) .set('timeFormat', program.time_format) + .set('hoursPerDay', program.hours_per_day) .set('output', program.output) .set('file', program.file) .set('query', program.query) @@ -90,12 +122,26 @@ config .set('proxy', program.proxy) .set('type', program.type) .set('subgroups', program.subgroups) - .set('_verbose', program.verbose); + .set('_verbose', program.verbose) + .set('_createDump', program.output === 'dump'); + +// date shortcuts +if (program.today) + config + .set('from', moment().startOf('day')) + .set('to', moment().endOf('day')); +if (program.this_week) + config + .set('from', moment().startOf('week')) + .set('to', moment().endOf('week')); +if (program.this_month) + config + .set('from', moment().startOf('month')) + .set('to', moment().endOf('month')); Cli.quiet = config.get('quiet'); Cli.verbose = config.get('_verbose'); - // create stuff let reports = new ReportCollection(config), master = new Report(config), @@ -116,11 +162,14 @@ if ((config.get('report').includes('issues') && !config.get('query').includes('i if ((config.get('report').includes('merge_requests') && !config.get('query').includes('merge_requests'))) { Cli.warn(`Merge Requests are included in the report but not queried.`); } +if (!config.get('project')) { + Cli.error(`Missing project(s) or group(s) namespace. Try this: gtt report "username/project-name"`); +} if (!Output[config.get('output')]) { - Cli.error(`The output ${config.get('output')} doesn't exist`); + Cli.error(`The output ${config.get('output')} doesn't exist. Available outputs: ${Object.keys(Output).join(',')}`); } if (config.get('output') === 'pdf' && !config.get('file')) { - Cli.error(`Cannot output a pdf to stdout`); + Cli.error(`Cannot output a pdf to stdout. You probably forgot to use the --file parameter`); } if (!config.get('from').isValid()) { Cli.error(`FROM is not in a valid ISO date format.`); @@ -128,6 +177,9 @@ if (!config.get('from').isValid()) { if (!config.get('to').isValid()) { Cli.error(`TO is not a in valid ISO date format.`); } +if (config.get('output') === 'xlsx' && !config.get('file')) { + Cli.error(`Cannot output an xlsx to stdout. You probably forgot to use the --file parameter`); +} // file prompt new Promise(resolve => { @@ -145,8 +197,9 @@ new Promise(resolve => { Cli.list(`${Cli.look} Resolving "${projectLabels}"`); let owner = new Owner(config); - owner - .parallel(projects, (project, done) => { + owner.authorized() + .catch(e => Cli.x(`Invalid access token!`, e)) + .then(() => owner.parallel(projects, (project, done) => { config.set('project', project); switch (config.get('type')) { @@ -155,7 +208,7 @@ new Promise(resolve => { reports.push(report); report.getProject() .then(() => done()) - .catch(e => done(e)); + .catch(e => Cli.x(`Project not found or no access rights "${projectLabels}".`, e)); break; case 'group': @@ -172,7 +225,7 @@ new Promise(resolve => { .catch(e => done(e)); break; } - }, 1) + }, 1)) .catch(e => reject(e)) .then(() => { config.set('project', projects); @@ -184,7 +237,6 @@ new Promise(resolve => { // get members and user columns .then(() => new Promise(resolve => { if (!config.get('userColumns')) return resolve(); - reports .forEach((report, done) => { report.project.members() @@ -300,4 +352,4 @@ new Promise(resolve => { .then(() => Cli.mark()) // time for a beer - .then(() => Cli.done()); \ No newline at end of file + .then(() => Cli.done()); diff --git a/src/gtt-resume.js b/src/gtt-resume.js new file mode 100755 index 0000000..baf3261 --- /dev/null +++ b/src/gtt-resume.js @@ -0,0 +1,24 @@ +const program = require('commander'); +const colors = require('colors'); +const moment = require('moment'); + +const Config = require('./include/file-config'); +const Cli = require('./include/cli'); +const Tasks = require('./include/tasks'); + +program + .arguments('[project]') + .option('--verbose', 'show verbose output') + .parse(process.argv); + +Cli.verbose = program.verbose; + +let config = new Config(process.cwd()).set('project', program.args[0]), + tasks = new Tasks(config); + +if (!config.get('project')) + Cli.error('No project set'); + +tasks.resume() + .then(frame => console.log(`Starting project ${config.get('project').magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue} at ${moment().format('HH:mm').green}`)) + .catch(error => Cli.error(error)); diff --git a/gtt-start.js b/src/gtt-start.js old mode 100644 new mode 100755 similarity index 80% rename from gtt-start.js rename to src/gtt-start.js index 326fe75..cdc53e5 --- a/gtt-start.js +++ b/src/gtt-start.js @@ -9,6 +9,8 @@ const Tasks = require('./include/tasks'); program .arguments('[project] [id]') .option('-t, --type ', 'specify resource type: issue, merge_request') + .option('-m', 'shorthand for --type=merge_request') + .option('-i', 'shorthand for --type=issue') .option('--verbose', 'show verbose output') .parse(process.argv); @@ -18,7 +20,13 @@ let config = new Config(process.cwd()), tasks = new Tasks(config), type = program.type ? program.type : 'issue', id = program.args.length === 1 ? parseInt(program.args[0]) : parseInt(program.args[1]), - project = program.args.length === 2 ? project = program.args[0] : null; + project = program.args.length === 2 ? program.args[0] : null; + +if (program.I) { + type = 'issue'; +} else if (program.M) { + type = 'merge_request'; +} if (program.args.length < 2 && !config.get('project')) Cli.error('No project set'); diff --git a/gtt-status.js b/src/gtt-status.js old mode 100644 new mode 100755 similarity index 99% rename from gtt-status.js rename to src/gtt-status.js index 6028ea7..ea8462b --- a/gtt-status.js +++ b/src/gtt-status.js @@ -6,7 +6,6 @@ const Config = require('./include/file-config'); const Cli = require('./include/cli'); const Tasks = require('./include/tasks'); - program .option('--verbose', 'show verbose output') .parse(process.argv); diff --git a/gtt-stop.js b/src/gtt-stop.js old mode 100644 new mode 100755 similarity index 50% rename from gtt-stop.js rename to src/gtt-stop.js index 67c69fa..f7e1485 --- a/gtt-stop.js +++ b/src/gtt-stop.js @@ -17,6 +17,11 @@ let config = new Config(__dirname), tasks.stop() .then(frames => { - frames.forEach(frame => console.log(`Stopping project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green} (id: ${frame.id})`)); + frames.forEach(frame => { + if(!frame.resource.new) + return console.log(`Stopping project ${frame.project.magenta} ${frame.resource.type.blue} ${('#' + frame.resource.id).blue}, started ${moment(frame.start).fromNow().green} (id: ${frame.id})`) + + console.log(`Stopping project ${frame.project.magenta} for new ${frame.resource.type} "${(frame.resource.id).blue}", started ${moment(frame.start).fromNow().green} (id: ${frame.id})`) + }); }) .catch(error => Cli.error(error)); \ No newline at end of file diff --git a/src/gtt-sync.js b/src/gtt-sync.js new file mode 100755 index 0000000..0c459c4 --- /dev/null +++ b/src/gtt-sync.js @@ -0,0 +1,43 @@ +const moment = require('moment'); +const program = require('commander'); + +const Config = require('./include/file-config'); +const Cli = require('./include/cli'); +const Tasks = require('./include/tasks'); +const Owner = require('./models/owner'); + +program + .option('-p --proxy ', 'use a proxy server with the given url') + .option('--url ', 'URL to GitLabs API') + .option('--token ', 'API access token') + .option('--insecure', 'don\'t check certificates') + .option('--verbose', 'show verbose output') + .parse(process.argv); + +Cli.verbose = program.verbose; + +let config = new Config(process.cwd()) + .set('url', program.url) + .set('token', program.token) + .set('insecure', program.insecure) + .set('proxy', program.proxy); + tasks = new Tasks(config), + owner = new Owner(config); + +tasks.syncInit() + .then(() => tasks.sync.frames.length === 0 ? process.exit(0) : null) + .then(() => owner.authorized()) + .catch(e => Cli.x(`Invalid access token!`, e)) + .then(() => { + Cli.bar(`${Cli.fetch} Fetching or creating issues & merge requests...`, tasks.sync.frames.length); + return tasks.syncResolve(Cli.advance); + }) + .then(() => { + Cli.bar(`${Cli.process} Processing issues & merge requests...`, tasks.sync.frames.length); + return tasks.syncNotes(Cli.advance); + }) + .then(() => { + Cli.bar(`${Cli.update} Syncing time records...`, tasks.sync.frames.length); + return tasks.syncUpdate(Cli.advance) + }) + .catch(error => Cli.x(error)); diff --git a/gtt.js b/src/gtt.js similarity index 70% rename from gtt.js rename to src/gtt.js index dfa9890..387c105 100755 --- a/gtt.js +++ b/src/gtt.js @@ -1,13 +1,16 @@ #!/usr/bin/env node +require('app-module-path').addPath(__dirname); -const version = '1.5.5'; +const version = require('../package.json').version; const program = require('commander'); program .version(version) .command('start [project] [id]', 'start monitoring time for the given project and resource id') + .command('create [project] [title]', 'start monitoring time for the given project and create a new issue or merge request with the given title') .command('status', 'shows if time monitoring is running') .command('stop', 'stop monitoring time') + .command('resume [project]', 'resume monitoring time for last stopped record') .command('cancel', 'cancel and discard active monitoring time') .command('log', 'log recorded time records') .command('sync', 'sync local time records to GitLab') diff --git a/include/cli.js b/src/include/cli.js old mode 100644 new mode 100755 similarity index 96% rename from include/cli.js rename to src/include/cli.js index 14a354e..3f9ac22 --- a/include/cli.js +++ b/src/include/cli.js @@ -18,6 +18,10 @@ class cli { /* * emojis */ + static get update() { + return '⏱'; + } + static get print() { return '🖨'; } @@ -44,7 +48,6 @@ class cli { static get party() { return '🥑'; - // return '🍺'; } /** @@ -238,9 +241,12 @@ class cli { if (this.data.project) return this.data.project; - let projects = _.uniq(_.filter(this.args, arg => !parseInt(arg))); + let projects = _.uniq(_.filter(this.args, arg => isNaN(new Number(arg)))); this.args = _.difference(this.args, projects); + if(projects.length == 0) + return null; + return this.data.project = projects; } diff --git a/include/config.js b/src/include/config.js old mode 100644 new mode 100755 similarity index 69% rename from include/config.js rename to src/include/config.js index 9ea56e3..62f82c1 --- a/include/config.js +++ b/src/include/config.js @@ -1,14 +1,18 @@ const moment = require('moment'); +const _ = require('underscore'); const Time = require('./../models/time'); +const EventEmitter = require('events'); const dates = ['from', 'to']; +const objectsWithDefaults = ['timeFormat', 'columns']; const defaults = { type: 'project', subgroups: false, url: 'https://gitlab.com/api/v4', token: false, proxy: false, + insecure: false, project: false, from: "1970-01-01", to: moment().format(), @@ -16,6 +20,8 @@ const defaults = { closed: false, milestone: false, hoursPerDay: 8, + daysPerWeek: 5, + weeksPerMonth: 4, issueColumns: ['iid', 'title', 'spent', 'total_estimate'], mergeRequestColumns: ['iid', 'title', 'spent', 'total_estimate'], recordColumns: ['user', 'date', 'type', 'iid', 'time'], @@ -33,20 +39,25 @@ const defaults = { noWarnings: false, quiet: false, showWithoutTimes: false, + timezone: "UTC", _perPage: 100, _parallel: 10, - _verbose: false + _verbose: false, + _checkToken: true, + _skipDescriptionParsing: false }; /** * basic config */ -class config { +class config extends EventEmitter { /** * construct */ constructor() { - this.data = defaults; + super(); + + this.data = _.extend({}, defaults); } /** @@ -69,21 +80,27 @@ class config { /** * get a value by the given key * @param key + * @param subKey * @returns {*} */ - get(key) { - if (!dates.includes(key)) return this.data[key]; + get(key, subKey = false) { + if (dates.includes(key)) + return moment(this.data[key]); + + if (objectsWithDefaults.includes(key) && _.isObject(this.data[key])) + return subKey && this.data[key][subKey] ? this.data[key][subKey] : defaults[key]; - return moment(this.data[key]); + return this.data[key]; } /** * get a human readable version of the given time * @param input + * @param timeFormat * @returns {string} */ - toHumanReadable(input) { - return Time.toHumanReadable(input, this.get('hoursPerDay'), this.get('timeFormat')); + toHumanReadable(input, timeFormat = false) { + return Time.toHumanReadable(input, this.get('hoursPerDay'), this.get('timeFormat', timeFormat)); } } diff --git a/include/file-config.js b/src/include/file-config.js old mode 100644 new mode 100755 similarity index 61% rename from include/file-config.js rename to src/include/file-config.js index f1e55e2..bb60408 --- a/include/file-config.js +++ b/src/include/file-config.js @@ -1,5 +1,8 @@ const fs = require('fs'); +const shell = require('shelljs'); const path = require('path'); +const os = require("os"); +const xdgBaseDir = require('xdg-basedir'); const config = require('./config'); const yaml = require('read-yaml'); const hash = require('hash-sum'); @@ -19,7 +22,9 @@ class fileConfig extends config { this.assertGlobalConfig(); this.workDir = workDir; this.data = Object.assign(this.data, this.localExists() ? this.parseLocal() : this.parseGlobal()); + this._dump = {}; this.cache = { + delete: this._cacheDelete, get: this._cacheGet, set: this._cacheSet, dir: this.cacheDir @@ -45,7 +50,21 @@ class fileConfig extends config { */ parseLocal() { try { - return yaml.sync(this.local, {}); + let local = Object.assign({extend: true}, yaml.sync(this.local, {})); + + if (local.extend === true) { + let global = this.parseGlobal(); + local = Object.assign(global ? global : {}, local); + } else if (local.extend) { + try { + local = Object.assign(yaml.sync(local.extend, {}), local); + } catch (e) { + console.log(`Error parsing configuration: "${local.extend}"`); + process.exit(1); + } + } + + return local; } catch (e) { console.log(`Error parsing configuration: "${this.local}"`); process.exit(1); @@ -55,10 +74,11 @@ class fileConfig extends config { localExists() { if (fs.existsSync(this.local)) return true; + let root = (os.platform() === "win32") ? process.cwd().split(path.sep)[0] + "\\" : "/"; let workDir = this.workDir; while (workDir) { workDir = path.dirname(workDir); - if (workDir === '/') workDir = ''; + if (workDir === root) workDir = ''; if (fs.existsSync(Fs.join(workDir, this.localConfigFile))) { this.workDir = workDir; return true; @@ -67,9 +87,15 @@ class fileConfig extends config { } assertGlobalConfig() { - if (!fs.existsSync(this.globalDir)) fs.mkdirSync(this.globalDir, '0644', true); - if (!fs.existsSync(this.frameDir)) fs.mkdirSync(this.frameDir, '0744', true); - if (!fs.existsSync(this.cacheDir)) fs.mkdirSync(this.cacheDir, '0744', true); + if(!fs.existsSync(this.globalDir) && fs.existsSync(this.oldGlobalDir)) { + fs.renameSync(this.oldGlobalDir, this.globalDir); + } + + + + if (!fs.existsSync(this.globalDir)) shell.mkdir('-p', this.globalDir); + if (!fs.existsSync(this.frameDir)) shell.mkdir('-p', this.frameDir); + if (!fs.existsSync(this.cacheDir)) shell.mkdir('-p', this.cacheDir); if (!fs.existsSync(this.global)) fs.appendFileSync(this.global, ''); } @@ -77,6 +103,13 @@ class fileConfig extends config { if (!this.localExists()) fs.appendFileSync(this.local, ''); } + _cacheDelete(key) { + let file = Fs.join(this.dir, hash(key)); + if (!fs.existsSync(file)) return false; + + return fs.unlinkSync(file); + } + _cacheGet(key) { let file = Fs.join(this.dir, hash(key)); if (!fs.existsSync(file)) return false; @@ -97,10 +130,14 @@ class fileConfig extends config { return '.gtt.yml'; } - get globalDir() { + get oldGlobalDir() { return Fs.join(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.gtt'); } + get globalDir() { + return Fs.join(xdgBaseDir.data, '.gtt'); + } + get frameDir() { return Fs.join(this.globalDir, 'frames'); } @@ -116,6 +153,15 @@ class fileConfig extends config { get local() { return Fs.join(this.workDir, this.localConfigFile); } + + setDump(key, value) { + this._dump[key] = value; + this.emit('dump-updated'); + } + + getDump(key) { + return this._dump[key]; + } } -module.exports = fileConfig; \ No newline at end of file +module.exports = fileConfig; diff --git a/include/filesystem.js b/src/include/filesystem.js old mode 100644 new mode 100755 similarity index 71% rename from include/filesystem.js rename to src/include/filesystem.js index 747bc7b..7de072f --- a/include/filesystem.js +++ b/src/include/filesystem.js @@ -3,6 +3,7 @@ const fs = require('fs'); const path = require('path'); const open = require('open'); const find = require('find-in-files'); +const child_process = require('child_process'); class filesystem { static find(pattern, dir) { @@ -22,7 +23,15 @@ class filesystem { } static open(file) { - return open(file); + let editor = process.env.VISUAL; + + if (editor || (editor = process.env.EDITOR)) { + return child_process.spawn(editor, [file], { + stdio: 'inherit' + }); + } else { + return open(file); + } } static join(...args) { @@ -38,4 +47,4 @@ class filesystem { } } -module.exports = filesystem; \ No newline at end of file +module.exports = filesystem; diff --git a/include/tasks.js b/src/include/tasks.js old mode 100644 new mode 100755 similarity index 65% rename from include/tasks.js rename to src/include/tasks.js index 38d7cc1..e28f7f6 --- a/include/tasks.js +++ b/src/include/tasks.js @@ -13,6 +13,11 @@ const classes = { merge_request: MergeRequest }; +const stop_condition = { + term: `"stop" ?: ?false`, + flags: "i" +}; + class tasks { constructor(config) { this.config = config; @@ -20,21 +25,29 @@ class tasks { } /** - * Filter frames that need an update and resolve merge_requests and issues - * respectively. + * Filter frames that need an update * @returns {Promise} */ - syncResolve() { + syncInit() { this.sync.frames = new FrameCollection(this.config); + // filter out frames, that don't need an update + this.sync.frames.filter(frame => !(Math.ceil(frame.duration) === _.reduce(frame.notes, (n, m) => (n + m.time), 0))); + + return new Promise(r => r()); + } + + /** + * Resolve merge_requests and issues + * respectively. + * @returns {Promise} + */ + syncResolve(callback) { this.sync.resources = { issue: {}, merge_request: {} }; - // filter out frames, that don't need an update - this.sync.frames.filter(frame => !(Math.ceil(frame.duration) === _.reduce(frame.notes, (n, m) => (n + m.time), 0))); - // resolve issues and merge requests return this.sync.frames.forEach((frame, done) => { let project = frame.project, @@ -47,8 +60,11 @@ class tasks { this.sync.resources[type][id] = new classes[type](this.config, {}); this.sync.resources[type][id] - .make(project, id) - .then(() => done()) + .make(project, id, frame.resource.new) + .then(() => { + if (callback) callback(); + done(); + }) .catch(error => done(`Could not resolve ${type} ${id} on "${project}"`)); }) } @@ -56,18 +72,24 @@ class tasks { /** * Get notes for all frames. */ - syncNotes() { + syncNotes(callback) { return this.sync.frames.forEach((frame, done) => { let project = frame.project, type = frame.resource.type, id = frame.resource.id, notes; - if ((notes = this.sync.resources[type][id].notes) && notes.length > 0) return; + if ((notes = this.sync.resources[type][id].notes) && notes.length > 0) { + if (callback) callback(); + return done(); + } this.sync.resources[type][id] .getNotes() - .then(() => done()) + .then(() => { + if (callback) callback(); + done(); + }) .catch(error => done(`Could not get notes from ${type} ${id} on "${project}"`)); }); } @@ -95,18 +117,23 @@ class tasks { return new Promise((resolve, reject) => { let resource = this.sync.resources[frame.resource.type][frame.resource.id]; - resource - .createTime(Math.ceil(time)) + resource.createTime(Math.ceil(time)) .then(() => resource.getNotes()) .then(() => { + if (frame.resource.new) { + delete frame.resource.new; + frame.resource.id = resource.data.iid; + } + frame.notes.push({ id: resource.notes[0].id, time: Math.ceil(time) }); - frame.write(); + + frame.write(true); resolve(); }) - .catch(error => reject(error)) + .catch(error => reject(error)); }); } @@ -116,7 +143,7 @@ class tasks { */ status() { return new Promise((resolve, reject) => { - Fs.find(`"stop": false`, this.config.frameDir) + Fs.find(stop_condition, this.config.frameDir) .then(frames => resolve(frames.map(file => Frame.fromFile(this.config, file)))) .catch(error => reject(error)); }); @@ -131,22 +158,54 @@ class tasks { let frames = {}, times = {}; - Fs.readDir(this.config.frameDir).forEach(file => { - let frame = Frame.fromFile(this.config, Fs.join(this.config.frameDir, file)); - if (frame.stop === false) return; - let date = moment(frame.start).format('YYYY-MM-DD'); + new FrameCollection(this.config) + .forEach((frame, done) => { + if (frame.stop === false) return done(); + let date = frame.date.format('YYYY-MM-DD'); + + if (!frames[date]) frames[date] = []; + if (!times[date]) times[date] = 0; + + frames[date].push(frame); + times[date] += Math.ceil(frame.duration); + + done(); + }) + .then(() => new Promise(r => { + resolve({frames, times}); + r(); + })) + .catch(error => reject(error)); + }); + } + + /** + * + * @returns {Promise} + */ + resume() { + return new Promise((resolve, reject) => { + let project = this.config.get('project'), + frames = new FrameCollection(this.config); - if (!frames[date]) frames[date] = []; - if (!times[date]) times[date] = 0; + if (!project) return reject("No project set."); - frames[date].push(frame); - times[date] += Math.ceil(frame.duration); - }); + frames + .filter(frame => frame.project === project) + .sort((a, b) => moment(a.stop).isBefore(moment(b.stop)) ? 1 : -1); - resolve({frames, times}) + let last = frames.frames[0]; + this.start(last.project, last.resource.type, last.resource.id) + .then(frame => resolve(frame)) + .catch(error => reject(error)); }); } + list(project, state, my) { + this.config.set('project', project); + return (new classes['issue'](this.config, {})).list(this.config.get('project'), state, my); + } + /** * * @param project @@ -158,7 +217,7 @@ class tasks { this.config.set('project', project); return new Promise((resolve, reject) => { - Fs.find(`"stop": false`, this.config.frameDir) + Fs.find(stop_condition, this.config.frameDir) .then(frames => { if (frames.length > 0) return reject("Already running. Please stop it first with 'gtt stop'."); @@ -175,7 +234,7 @@ class tasks { */ stop() { return new Promise((resolve, reject) => { - Fs.find(`"stop": false`, this.config.frameDir) + Fs.find(stop_condition, this.config.frameDir) .then(frames => { if (frames.length === 0) return reject('No projects started.'); @@ -194,7 +253,7 @@ class tasks { */ cancel() { return new Promise((resolve, reject) => { - Fs.find(`"stop": false`, this.config.frameDir) + Fs.find(stop_condition, this.config.frameDir) .then(frames => { if (frames.length === 0) return reject('No projects started.'); @@ -211,4 +270,4 @@ class tasks { } } -module.exports = tasks; \ No newline at end of file +module.exports = tasks; diff --git a/models/base.js b/src/models/base.js old mode 100644 new mode 100755 similarity index 61% rename from models/base.js rename to src/models/base.js index dac2c00..754aac5 --- a/models/base.js +++ b/src/models/base.js @@ -1,6 +1,8 @@ const request = require('request-promise-native'); const url = require('url'); const async = require('async'); +const crypto = require('crypto'); +const throttle = require('throttled-queue')(10, 1000); /** * base model @@ -19,6 +21,7 @@ class base { this._perPage = this.config ? this.config.get('_perPage') : 100; this._parallel = this.config ? this.config.get('_parallel') : 4; this._proxy = this.config && this.config.get('proxy') ? this.config.get('proxy') : undefined; + this._insecure = this.config && this.config.get('insecure') ? this.config.get('insecure') : false; } /** @@ -28,17 +31,26 @@ class base { * @returns {*} */ post(path, data) { + let key = base.createDumpKey(path, data); + if (this.config.dump) return this.getDump(key); + data.private_token = this.token; - return request.post(`${this.url}${path}`, { - json: true, - body: data, - proxy: this._proxy, - resolveWithFullResponse: true, - headers: { - 'PRIVATE-TOKEN': this.token - } - }); + return new Promise((resolve, reject) => throttle(() => { + request.post(`${this.url}${path}`, { + json: true, + body: data, + insecure: this._insecure, + proxy: this._proxy, + resolveWithFullResponse: true, + headers: { + 'PRIVATE-TOKEN': this.token + } + }).then(response => { + if (this.config.get('_createDump')) this.setDump(response, key); + resolve(response); + }).catch(e => reject(e)); + })); } /** @@ -49,17 +61,26 @@ class base { * @returns {Promise} */ get(path, page = 1, perPage = this._perPage) { + let key = base.createDumpKey(path, page, perPage); + if (this.config.dump) return this.getDump(key); + path += (path.includes('?') ? '&' : '?') + `private_token=${this.token}`; path += `&page=${page}&per_page=${perPage}`; - return request(`${this.url}${path}`, { - json: true, - proxy: this._proxy, - resolveWithFullResponse: true, - headers: { - 'PRIVATE-TOKEN': this.token - } - }); + return new Promise((resolve, reject) => throttle(() => { + request(`${this.url}${path}`, { + json: true, + insecure: this._insecure, + proxy: this._proxy, + resolveWithFullResponse: true, + headers: { + 'PRIVATE-TOKEN': this.token + } + }).then(response => { + if (this.config.get('_createDump')) this.setDump(response, key); + resolve(response); + }).catch(e => reject(e)); + })); } /** @@ -121,6 +142,27 @@ class base { }, runners); } + /** + * save the given response to dump + * @param response + * @param key + */ + setDump(response, key) { + this.config.setDump(key, { + headers: response.headers, + body: response.body + }); + } + + /** + * get from dump + * @param key + * @returns {Promise} + */ + getDump(key) { + return new Promise(r => r(this.config.getDump(key))); + } + /** * create a task list to get all pages from * the given path @@ -139,6 +181,14 @@ class base { return tasks; } + + /** + * create a key representing a request + * @param args + */ + static createDumpKey(...args) { + return crypto.createHash('md5').update(JSON.stringify(args)).digest("hex"); + } } -module.exports = base; \ No newline at end of file +module.exports = base; diff --git a/src/models/baseFrame.js b/src/models/baseFrame.js new file mode 100755 index 0000000..ef4e940 --- /dev/null +++ b/src/models/baseFrame.js @@ -0,0 +1,100 @@ +const Config = require('../include/config'); + +const moment = require('moment-timezone'); +const Hashids = require('hashids'); +const hashids = new Hashids(); + +class baseFrame { + /** + * constructor. + * @param config + * @param id + * @param type + */ + constructor(config, id, type) { + this.config = config; + this.project = config.get('project'); + this.resource = {id, type}; + + if(typeof id === 'string' || id instanceof String) + this.resource.new = true; + + this.id = baseFrame.generateId(); + this._start = false; + this._stop = false; + this.timezone = config.get('timezone'); + this.notes = []; + } + + static fromJson(config, json) { + let frame = new this(config, json.resource.id, json.resource.type); + frame.project = json.project; + frame.id = json.id; + frame._start = json.start; + frame._stop = json.stop; + frame.notes = json.notes; + frame.timezone = json.timezone; + frame.modified = json.modified; + frame.validate(); + + return frame; + } + + validate() { + moment.suppressDeprecationWarnings = true; + + if(!moment(this._start).isValid()) + throw `Error: Start date is not in a valid ISO date format!`; + + if(this._stop && !moment(this._stop).isValid()) + throw `Error: Stop date is not in a valid ISO date format!`; + + moment.suppressDeprecationWarnings = false; + } + + _getCurrentDate() { + if(this.timezone) + return moment().tz(this.timezone).format(); + + return moment(); + } + + static copy(frame) { + return this.fromJson(Object.assign(new Config, frame.config), { + id: frame.id, + project: frame.project, + resource: frame.resource, + notes: frame.notes, + start: frame._start, + stop: frame._stop, + timezone: frame.timezone, + modified: frame.modified + }); + } + + get duration() { + return moment(this.stop).diff(this.start) / 1000; + } + + get date() { + return this.start; + } + + get start() { + return this.timezone ? moment(this._start).tz(this.timezone) : moment(this._start); + } + + get stop() { + return this.timezone ? this._stop ? moment(this._stop).tz(this.timezone) : false : (this._stop ? moment(this._stop) : false ); + } + + /** + * generate a unique id + * @returns {number} + */ + static generateId() { + return hashids.encode(new Date().getTime()); + } +} + +module.exports = baseFrame; diff --git a/src/models/frame.js b/src/models/frame.js new file mode 100755 index 0000000..6eb1e84 --- /dev/null +++ b/src/models/frame.js @@ -0,0 +1,54 @@ +const fs = require('fs'); +const path = require('path'); +const moment = require('moment'); +const BaseFrame = require('./baseFrame.js'); + +class frame extends BaseFrame { + static fromFile(config, file) { + return frame.fromJson(config, JSON.parse(fs.readFileSync(file))); + } + + startMe() { + this._start = this._getCurrentDate(); + this.write(); + + return this; + } + + stopMe() { + this._stop = this._getCurrentDate(); + this.write(); + + return this; + } + + /** + * assert file exists + */ + assertFile() { + if (!fs.existsSync(this.file)) fs.appendFileSync(this.file, ''); + } + + /** + * write data to file + */ + write(skipModified) { + if (fs.existsSync(this.file)) fs.unlinkSync(this.file); + fs.appendFileSync(this.file, JSON.stringify({ + id: this.id, + project: this.project, + resource: this.resource, + notes: this.notes, + start: this._start, + stop: this._stop, + timezone: this.timezone, + modified: skipModified ? this.modified : moment() + }, null, "\t")); + } + + get file() { + return path.join(this.config.frameDir, this.id + '.json'); + } +} + +module.exports = frame; diff --git a/src/models/frameCollection.js b/src/models/frameCollection.js new file mode 100755 index 0000000..1bffeb9 --- /dev/null +++ b/src/models/frameCollection.js @@ -0,0 +1,57 @@ +const Base = require('./base'); +const Frame = require('./frame'); +const Fs = require('./../include/filesystem'); + +class frameCollection extends Base { + constructor(config) { + super(config); + + this.frames = + Fs.readDir(config.frameDir) + .map(file => { + try { + return Frame.fromFile(this.config, Fs.join(this.config.frameDir, file)); + } catch (e) { + console.log(e); + throw `Error parsing frame file: ${file}` + } + }) + .filter(frame => frame); + } + + sort(func) { + this.frames.sort(func); + + return this; + } + + filter(func) { + let arr = []; + + this.frames.forEach(frame => { + if (frame.stop === false) { + return false; + } + + if (func(frame)) { + arr.push(frame); + } + }); + + this.frames = arr; + + return this; + } + + forEach(iterator) { + let promise = this.parallel(this.frames, iterator); + + return promise; + } + + get length() { + return this.frames.length; + } +} + +module.exports = frameCollection; \ No newline at end of file diff --git a/models/hasTimes.js b/src/models/hasTimes.js old mode 100644 new mode 100755 similarity index 53% rename from models/hasTimes.js rename to src/models/hasTimes.js index f4c3c9d..87cd1fe --- a/models/hasTimes.js +++ b/src/models/hasTimes.js @@ -4,8 +4,8 @@ const moment = require('moment'); const Base = require('./base'); const Time = require('./time'); -const regex = /added (.*) of time spent/i; -const subRegex = /subtracted (.*) of time spent/i; +const regex = /added (.*) of time spent(?: at (.*))?/i; +const subRegex = /subtracted (.*) of time spent(?: at (.*))?/i; const removeRegex = /Removed time spent/i; /** @@ -55,9 +55,11 @@ class hasTimes extends Base { * @returns {Promise} */ getTimes() { - let times = []; - let timeSpent = 0; - let timeUsers = {}; + let times = [], + timeSpent = 0, + totalTimeSpent = 0, + timeUsers = {}, + timeFormat = this.config.get('timeFormat', this._type); // sort by created at this.notes.sort((a, b) => { @@ -66,24 +68,37 @@ class hasTimes extends Base { }); let promise = this.parallel(this.notes, (note, done) => { - let created = moment(note.created_at), match, subMatch, removeMatch; + let created = moment(note.created_at), match, subMatch; - if ( + if ( // // filter out user notes !note.system || + // filter out notes that are no time things + !(match = regex.exec(note.body)) && !(subMatch = subRegex.exec(note.body)) && !removeRegex.exec(note.body) + ) return done(); + + // change created date when explicitly defined + if(match && match[2]) created = moment(match[2]); + if(subMatch && subMatch[2]) created = moment(subMatch[2]); + + // create a time string and a time object + let timeString = match ? match[1] : (subMatch ? `-${subMatch[1]}` : `-${Time.toHumanReadable(timeSpent)}`); + let time = new Time(null, created, note, this, this.config); + time.seconds = Time.parse(timeString, 8, 5, 4); + + // add to total time spent + totalTimeSpent += time.seconds; + + if ( // // only include times by the configured user (this.config.get('user') && this.config.get('user') !== note.author.username) || // filter out times that are not in the given time frame - !(created.isSameOrAfter(moment(this.config.get('from'))) && created.isSameOrBefore(moment(this.config.get('to')))) || - // filter out notes that are no time things - !(match = regex.exec(note.body)) && !(subMatch = subRegex.exec(note.body)) && !(removeMatch = removeRegex.exec(note.body)) + !(created.isSameOrAfter(moment(this.config.get('from'))) && created.isSameOrBefore(moment(this.config.get('to')))) ) return done(); if (!timeUsers[note.author.username]) timeUsers[note.author.username] = 0; - let timeString = match ? match[1] : (subMatch ? `-${subMatch[1]}` : `-${Time.toHumanReadable(timeSpent, this.config.get('hoursPerDay'))}`); - let time = new Time(timeString, note, this, this.config); - + // add to time spent & add to user specific time spent timeSpent += time.seconds; timeUsers[note.author.username] += time.seconds; @@ -93,8 +108,33 @@ class hasTimes extends Base { done(); }); + promise = promise.then(() => new Promise(resolve => { + let created = moment(this.data.created_at); + + if ( // + // skip if description parsing is disabled + this.config.get('_skipDescriptionParsing') || + // or time stats are not available + !this.data.time_stats || !this.data.time_stats.total_time_spent || + // or the total time matches + !this.data.time_stats || + totalTimeSpent === this.data.time_stats.total_time_spent || + // or the user is filtered out + (this.config.get('user') && this.config.get('user') !== this.data.author.username) || + // or the issue is not within the given time frame + !(created.isSameOrAfter(moment(this.config.get('from'))) && created.isSameOrBefore(moment(this.config.get('to')))) + ) return resolve(); + + let difference = this.data.time_stats.total_time_spent - totalTimeSpent, + note = Object.assign({noteable_type: this._typeSingular}, this.data); + + times.unshift(new Time(Time.toHumanReadable(difference, this.config.get('hoursPerDay')), null, note, this, this.config)); + + resolve(); + })); + promise.then(() => { - _.each(timeUsers, (time, name) => this[`time_${name}`] = this.config.toHumanReadable(time)); + _.each(timeUsers, (time, name) => this[`time_${name}`] = Time.toHumanReadable(time, this.config.get('hoursPerDay'), timeFormat)); this.timeSpent = timeSpent; this.times = times }); @@ -103,4 +143,4 @@ class hasTimes extends Base { } } -module.exports = hasTimes; \ No newline at end of file +module.exports = hasTimes; diff --git a/models/issue.js b/src/models/issue.js old mode 100644 new mode 100755 similarity index 73% rename from models/issue.js rename to src/models/issue.js index f43a10c..adeed56 --- a/models/issue.js +++ b/src/models/issue.js @@ -12,8 +12,15 @@ class issue extends hasTimes { this.data = data; } - make(project, id) { - let promise = this.get(`projects/${encodeURIComponent(project)}/issues/${id}`); + make(project, id, create = false) { + let promise; + + if (create) { + promise = this.post(`projects/${encodeURIComponent(project)}/issues`, {title: id}); + } else { + promise = this.get(`projects/${encodeURIComponent(project)}/issues/${id}`); + } + promise.then(issue => { this.data = issue.body; return promise; @@ -64,7 +71,7 @@ class issue extends hasTimes { } get closed() { - return !!this.data.closed; + return this.data.state === 'closed'; } get updated_at() { @@ -80,20 +87,28 @@ class issue extends hasTimes { } get spent() { - return this.config.toHumanReadable(this.timeSpent); + return this.config.toHumanReadable(this.timeSpent, this._type); + } + + get due_date() { + return this.data.due_date ? moment(this.data.due_date): null; } get total_spent() { - return this.stats ? this.config.toHumanReadable(this.stats.total_time_spent) : null; + return this.stats ? this.config.toHumanReadable(this.stats.total_time_spent, this._type) : null; } get total_estimate() { - return this.stats ? this.config.toHumanReadable(this.stats.time_estimate) : null; + return this.stats ? this.config.toHumanReadable(this.stats.time_estimate, this._type) : null; } get _type() { return 'issues'; } + + get _typeSingular() { + return 'Issue'; + } } -module.exports = issue; \ No newline at end of file +module.exports = issue; diff --git a/models/mergeRequest.js b/src/models/mergeRequest.js old mode 100644 new mode 100755 similarity index 77% rename from models/mergeRequest.js rename to src/models/mergeRequest.js index 6fc92d2..5ea2f95 --- a/models/mergeRequest.js +++ b/src/models/mergeRequest.js @@ -9,8 +9,15 @@ class mergeRequest extends hasTimes { this.data = data; } - make(project, id) { - let promise = this.get(`projects/${encodeURIComponent(project)}/merge_requests/${id}`); + make(project, id, create = false) { + let promise; + + if (create) { + promise = this.post(`projects/${encodeURIComponent(project)}/merge_requests`, {title: id}); + } else { + promise = this.get(`projects/${encodeURIComponent(project)}/merge_requests/${id}`); + } + promise.then(issue => { this.data = issue.body; return promise; @@ -73,20 +80,24 @@ class mergeRequest extends hasTimes { } get spent() { - return this.config.toHumanReadable(this.timeSpent); + return this.config.toHumanReadable(this.timeSpent, this._type); } get total_spent() { - return this.stats ? this.config.toHumanReadable(this.stats.total_time_spent) : null; + return this.stats ? this.config.toHumanReadable(this.stats.total_time_spent, this._type) : null; } get total_estimate() { - return this.stats ? this.config.toHumanReadable(this.stats.time_estimate) : null; + return this.stats ? this.config.toHumanReadable(this.stats.time_estimate, this._type) : null; } get _type() { return 'merge_requests'; } + + get _typeSingular() { + return 'Merge Request'; + } } module.exports = mergeRequest; \ No newline at end of file diff --git a/models/owner.js b/src/models/owner.js old mode 100644 new mode 100755 similarity index 66% rename from models/owner.js rename to src/models/owner.js index 69d58c1..55bdd68 --- a/models/owner.js +++ b/src/models/owner.js @@ -13,17 +13,36 @@ class owner extends Base { this.users = []; } + /** + * is authorized? + * @returns {Promise} + */ + authorized() { + if (!this.config.get('_checkToken')) return new Promise(r => r()); + + return new Promise((resolve, reject) => { + this.get('broadcast_messages') + .then(() => resolve()) + .catch(e => { + if (e.statusCode === 403) resolve(); + reject(e); + }); + }); + } + /** * query and set the group * @returns {Promise} */ getGroup() { return new Promise((resolve, reject) => { - this.get(`groups/?search=${encodeURIComponent(this.config.get('project'))}`) - .then(group => { - if (group.body.length === 0) return reject('Group not found'); - let filtered = group.body.filter(u => u.path === this.config.get('project')); - if (filtered.length === 0) return reject(); + this.get(`groups`) + .then(groups => { + if (groups.body.length === 0) return reject('Group not found'); + groups = groups.body; + + let filtered = groups.filter(group => group.full_path === this.config.get('project')); + if (filtered.length === 0) return reject('Group not found'); this.groups = this.groups.concat(filtered); resolve(); }) @@ -39,9 +58,11 @@ class owner extends Base { return new Promise((resolve, reject) => { this.get(`groups`) .then(groups => { - if (groups.body.length === 0) return reject(); - let filtered = groups.body.filter(u => this.groups.map(g => g.id).indexOf(u.parent_id) !== -1); - if (filtered.length === 0) return reject(); + if (groups.body.length === 0) return resolve(); + + let filtered = this._filterGroupsByParents(groups.body, this.groups.map(g => g.id)); + if (filtered.length === 0) return resolve(); + this.groups = this.groups.concat(filtered); resolve(); }) @@ -49,6 +70,18 @@ class owner extends Base { }); } + _filterGroupsByParents(groups, parents) { + let filtered = groups.filter(group => { + return parents.indexOf(group.parent_id) !== -1; + }); + + if (filtered.length !== 0) { + filtered = filtered.concat(this._filterGroupsByParents(groups, filtered.map(g => g.id))); + } + + return filtered; + } + // /** // * query and set the user // * @returns {Promise} diff --git a/src/models/project.js b/src/models/project.js new file mode 100755 index 0000000..eba3d5b --- /dev/null +++ b/src/models/project.js @@ -0,0 +1,70 @@ +const Base = require('./base'); + +/** + * project model + */ +class project extends Base { + /** + * construct + * @param config + * @param data + */ + constructor(config, data) { + super(config); + this.data = data; + this.projectMembers = data.members ? data.members : []; + } + + /** + * make + * @param name + */ + make(name) { + let promise = this.get(`projects/${encodeURIComponent(name)}`); + promise.then(project => this.data = project.body); + + return promise; + } + + /** + * set members + * @returns {Promise} + */ + members() { + return new Promise((resolve, reject) => { + this.get(`projects/${this.id}/members`) + .then(response => { + this.projectMembers = this.projectMembers.concat(response.body); + return new Promise(r => r()); + }) + .then(() => { + if (!this.data.namespace || !this.data.namespace.kind || this.data.namespace.kind !== "group") return resolve(); + + this.get(`groups/${this.data.namespace.id}/members`) + .then(response => { + this.projectMembers = this.projectMembers.concat(response.body); + resolve(); + }) + .catch(e => reject(e)); + }) + .catch(e => reject(e)); + }); + } + + /* + * properties + */ + get id() { + return this.data.id; + } + + get name() { + return this.data.path_with_namespace; + } + + get users() { + return this.projectMembers.map(member => member.username); + } +} + +module.exports = project; \ No newline at end of file diff --git a/models/report.js b/src/models/report.js old mode 100644 new mode 100755 similarity index 100% rename from models/report.js rename to src/models/report.js diff --git a/models/reportCollection.js b/src/models/reportCollection.js old mode 100644 new mode 100755 similarity index 100% rename from models/reportCollection.js rename to src/models/reportCollection.js diff --git a/models/time.js b/src/models/time.js old mode 100644 new mode 100755 similarity index 66% rename from models/time.js rename to src/models/time.js index 29a3eb9..abb0ae2 --- a/models/time.js +++ b/src/models/time.js @@ -2,9 +2,11 @@ const _ = require('underscore'); const moment = require('moment'); const defaultTimeFormat = '[%sign][%days>d ][%hours>h ][%minutes>m ][%seconds>s]'; -const mappings = ['complete', 'sign', 'weeks', 'days', 'hours', 'minutes', 'seconds']; -const regex = /^(?:([-])\s*)?(?:(\d+)w\s*)?(?:(\d+)d\s*)?(?:(\d+)h\s*)?(?:(\d+)m\s*)?(?:(\d+)s\s*)?$/; +const mappings = ['complete', 'sign', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds']; +const regex = /^(?:([-])\s*)?(?:(\d+)mo\s*)?(?:(\d+)w\s*)?(?:(\d+)d\s*)?(?:(\d+)h\s*)?(?:(\d+)m\s*)?(?:(\d+)s\s*)?$/; const conditionalRegex = /(\[\%([^\>\]]*)\>([^\]]*)\])/ig; +const roundedRegex = /(\[\%([^\>\]]*)\:([^\]]*)\])/ig; +const conditionalSimpleRegex = /([0-9]*)\>(.*)/ig; const defaultRegex = /(\[\%([^\]]*)\])/ig; Number.prototype.padLeft = function (n, str) { @@ -22,11 +24,17 @@ class time { * @param parent * @param config */ - constructor(timeString, note, parent, config) { + constructor(timeString, date = null, note, parent, config) { this.data = note; + this._date = date; this.parent = parent; this.config = config; - this.seconds = time.parse(timeString, this._hoursPerDay); + + if(!timeString) { + return; + } + + this.seconds = time.parse(timeString, this._hoursPerDay, this._daysPerWeek, this._weeksPerMonth); } /* @@ -41,7 +49,7 @@ class time { } get date() { - return moment(this.data.created_at); + return this._date ? moment(this._date) : moment(this.data.created_at); } get type() { @@ -61,20 +69,30 @@ class time { } get _timeFormat() { - return this.config && this.config.get('timeFormat') ? this.config.get('timeFormat') : ''; + return this.config && this.config.get('timeFormat', 'records') ? this.config.get('timeFormat', 'records') : ''; } get _hoursPerDay() { return this.config && this.config.get('hoursPerDay') ? parseInt(this.config.get('hoursPerDay')) : 8; } + get _daysPerWeek() { + return this.config && this.config.get('daysPerWeek') ? parseInt(this.config.get('daysPerWeek')) : 5; + } + + get _weeksPerMonth() { + return this.config && this.config.get('weeksPerMonth') ? parseInt(this.config.get('weeksPerMonth')) : 4; + } + /** * parse human readable to seconds * @param string * @param hoursPerDay + * @param daysPerWeek + * @param weeksPerMonth * @returns {*} */ - static parse(string, hoursPerDay = 8) { + static parse(string, hoursPerDay = 8, daysPerWeek = 5, weeksPerMonth = 4) { let match, parsed; if ((match = regex.exec(string)) === null) return false; @@ -84,7 +102,8 @@ class time { + (parseInt(parsed.minutes) * 60) + (parseInt(parsed.hours) * 60 * 60) + (parseInt(parsed.days) * hoursPerDay * 60 * 60) - + (parseInt(parsed.weeks) * 5 * hoursPerDay * 60 * 60)); + + (parseInt(parsed.weeks) * daysPerWeek * hoursPerDay * 60 * 60) + + (parseInt(parsed.months) * weeksPerMonth * daysPerWeek * hoursPerDay * 60 * 60)); } /** @@ -121,6 +140,20 @@ class time { inserts.seconds = ((input % secondsInADay) % secondsInAnHour) % secondsInAMinute; inserts.Seconds = inserts.seconds.padLeft(2, 0); + // rounded + while ((match = roundedRegex.exec(format)) !== null) { + if (match.index === roundedRegex.lastIndex) roundedRegex.lastIndex++; + let time, conditionalMatch, decimals = match[3]; + + if ((conditionalMatch = conditionalSimpleRegex.exec(decimals)) !== null) { + decimals = conditionalMatch[1] + } + + decimals = parseInt(decimals); + time = Math.ceil(inserts[match[2]] * Math.pow(10, decimals)) / Math.pow(10, decimals); + output = output.replace(match[0], time !== 0 && conditionalMatch ? time + conditionalMatch[2] : time); + } + // conditionals while ((match = conditionalRegex.exec(format)) !== null) { if (match.index === conditionalRegex.lastIndex) conditionalRegex.lastIndex++; diff --git a/output/base.js b/src/output/base.js old mode 100644 new mode 100755 similarity index 87% rename from output/base.js rename to src/output/base.js index 279359a..297b682 --- a/output/base.js +++ b/src/output/base.js @@ -90,7 +90,7 @@ class base { */ toFile(file, resolve) { fs.writeFileSync(file, this.out); - resolve(); + if (resolve) resolve(); } /** @@ -120,15 +120,28 @@ class base { totalEstimate += parseInt(issue.stats.time_estimate); totalSpent += parseInt(issue.stats.total_time_spent); }); + + this.report[type].sort((a, b) => { + if (a.iid === b.iid) return 0; + + return (a.iid - b.iid) < 0 ? 1 : -1; + }); }); + this.times = times; - this.users = _.mapObject(users, user => this.config.toHumanReadable(user)); - this.projects = _.mapObject(projects, project => this.config.toHumanReadable(project)); + this.times.sort((a, b) => { + if (a.date.isSame(b.date)) return 0; + + return a.date.isBefore(b.date) ? 1 : -1; + }); + + this.users = _.mapObject(users, user => this.config.toHumanReadable(user, 'stats')); + this.projects = _.mapObject(projects, project => this.config.toHumanReadable(project, 'stats')); this.stats = { - 'total estimate': this.config.toHumanReadable(totalEstimate), - 'total spent': this.config.toHumanReadable(totalSpent), - 'spent': this.config.toHumanReadable(spent) + 'total estimate': this.config.toHumanReadable(totalEstimate, 'stats'), + 'total spent': this.config.toHumanReadable(totalSpent, 'stats'), + 'spent': this.config.toHumanReadable(spent, 'stats') }; } diff --git a/output/csv.js b/src/output/csv.js old mode 100644 new mode 100755 similarity index 100% rename from output/csv.js rename to src/output/csv.js diff --git a/src/output/dump.js b/src/output/dump.js new file mode 100755 index 0000000..cbc732f --- /dev/null +++ b/src/output/dump.js @@ -0,0 +1,30 @@ +const fs = require('fs'); +const Config = require('../include/config'); + +class dump { + constructor(config, report) { + let configCopy = new Config(); + configCopy.data = Object.assign({}, config.data); + configCopy._dump = config._dump; + + configCopy.set('url', null, true); + configCopy.set('token', null, true); + configCopy.set('_createDump', false); + configCopy.workDir = null; + configCopy.cache = null; + + fs.writeFileSync(configCopy.get('file'), JSON.stringify(configCopy)); + } + + make() { + } + + toStdOut() { + } + + toFile(file, resolve) { + if (resolve) resolve(); + } +} + +module.exports = dump; \ No newline at end of file diff --git a/output/markdown.js b/src/output/markdown.js old mode 100644 new mode 100755 similarity index 100% rename from output/markdown.js rename to src/output/markdown.js diff --git a/output/pdf.js b/src/output/pdf.js old mode 100644 new mode 100755 similarity index 100% rename from output/pdf.js rename to src/output/pdf.js diff --git a/output/styles/highlight/default.css b/src/output/styles/highlight/default.css old mode 100644 new mode 100755 similarity index 100% rename from output/styles/highlight/default.css rename to src/output/styles/highlight/default.css diff --git a/output/styles/layout/default.css b/src/output/styles/layout/default.css old mode 100644 new mode 100755 similarity index 100% rename from output/styles/layout/default.css rename to src/output/styles/layout/default.css diff --git a/output/table.js b/src/output/table.js old mode 100644 new mode 100755 similarity index 100% rename from output/table.js rename to src/output/table.js diff --git a/src/output/xlsx.js b/src/output/xlsx.js new file mode 100644 index 0000000..9581ad1 --- /dev/null +++ b/src/output/xlsx.js @@ -0,0 +1,108 @@ +const _ = require('underscore'); +const fs = require('fs'); +const path = require('path'); +const XLSX = require('xlsx'); + +const Base = require('./base'); +const Cli = require('./../include/cli'); + +/** + * xlsx output + */ +class xlsx extends Base { + + makeStats() { + let stats = [[], []]; + + _.each(this.stats, (time, name) => { + stats[0].push(name); + stats[1].push(time); + }); + + if (this.projects.length > 1) { + _.each(this.projects, (time, name) => { + stats[0].push(name); + stats[1].push(time); + }); + } + + _.each(this.users, (time, name) => { + stats[0].push(name); + stats[1].push(time); + }); + + this.xlsxStats = XLSX.utils.aoa_to_sheet(stats); + } + + makeIssues() { + let issues = []; + issues.push(this.config.get('issueColumns')); + this.report.issues.forEach(issue => issues.push(this.prepare(issue, this.config.get('issueColumns')))); + + this.xlsxIssues = XLSX.utils.aoa_to_sheet(issues); + } + + makeMergeRequests() { + let mergeRequests = []; + mergeRequests.push(this.config.get('mergeRequestColumns')); + this.report.mergeRequests.forEach(mergeRequest => mergeRequests.push(this.prepare(mergeRequest, this.config.get('mergeRequestColumns')))); + + this.xlsxMergeRequests = XLSX.utils.aoa_to_sheet(mergeRequests); + } + + makeRecords() { + let times = []; + times.push(this.config.get('recordColumns')); + this.times.forEach(time => times.push(this.prepare(time, this.config.get('recordColumns')))); + + this.xlsxRecords = XLSX.utils.aoa_to_sheet(times); + } + + toFile(file, resolve) { + let fileName = path.basename(file); + let extName = path.extname(file); + let workbook = XLSX.utils.book_new(); + + if (this.config.get('report').includes('stats')) { + XLSX.utils.book_append_sheet(workbook, this.xlsxStats, 'Stats'); + } + + if (this.config.get('report').includes('issues')) { + XLSX.utils.book_append_sheet(workbook, this.xlsxIssues, 'Issues'); + } + + if (this.config.get('report').includes('merge_requests')) { + XLSX.utils.book_append_sheet(workbook, this.xlsxMergeRequests, 'Merge Requests'); + } + + if (this.config.get('report').includes('records')) { + XLSX.utils.book_append_sheet(workbook, this.xlsxRecords, 'Records'); + } + + XLSX.writeFile(workbook, fileName); + + resolve(); + } + + toStdOut() { + Cli.error(`Can't output xlsx to std out`); + } + + /** + * prepare the given object by converting numeric + * columns/properties as numbers instead of strings + * on top of what the parent method already does + * + * suboptimally done here to avoid impacts on other outputs + * + * @param obj + * @param columns + * @returns {Array} + */ + prepare(obj = {}, columns = []) { + let formattedObj = super.prepare(obj, columns); + return formattedObj.map(field => isNaN(field) ? field : Number(field)); + } +} + +module.exports = xlsx; diff --git a/upgrade.md b/upgrade.md old mode 100644 new mode 100755 index 179ae02..5f898ca --- a/upgrade.md +++ b/upgrade.md @@ -1,5 +1,10 @@ # gtt +### upgrading from < 1.5.* to 1.6.* + +Starting with version `1.6` the local configuration file will extend the global configuration by default. If you +don't want this behaviour, make sure to use `extend: false` in your local config. + ### upgrading from 0.* I built gtt originally in PHP because I'm a PHP dev and I chose the language I'm most familiar diff --git a/yarn.lock b/yarn.lock old mode 100644 new mode 100755 index a5a7dfb..8dfdeda --- a/yarn.lock +++ b/yarn.lock @@ -2,12 +2,71 @@ # yarn lockfile v1 -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" +"@babel/parser@7.2.3": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + +"@sinonjs/formatio@^2.0.0": + version "2.0.0" + resolved "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" + dependencies: + samsam "1.3.0" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +adler-32@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25" + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + +ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: co "^4.6.0" - json-stable-stringify "^1.0.1" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ajv@^6.5.5: + version "6.7.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" ansi-regex@^2.0.0: version "2.1.1" @@ -17,9 +76,19 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" dependencies: sprintf-js "~1.0.2" @@ -30,9 +99,41 @@ argparse@~0.1.15: underscore "~1.7.0" underscore.string "~2.4.0" +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + dependencies: + safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" @@ -42,15 +143,23 @@ assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -async@^1.5.2: +assertion-error@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" +async@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" dependencies: - lodash "^4.14.0" + lodash "^4.17.10" async@~0.9.0: version "0.9.2" @@ -64,6 +173,10 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + autolinker@~0.15.0: version "0.15.3" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" @@ -72,17 +185,44 @@ aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + aws4@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + version "1.7.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +aws4@^1.6.0, aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + +babel-runtime@6.26.0, babel-runtime@~6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" dependencies: tweetnacl "^0.14.3" @@ -92,16 +232,88 @@ boom@2.x.x: dependencies: hoek "2.x.x" +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + brace-expansion@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" -buffer-shims@~1.0.0: +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + +buffer-from@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" + +build@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/build/-/build-0.1.4.tgz#707fe026ffceddcacbfdcdf356eafda64f151046" + dependencies: + cssmin "0.3.x" + jsmin "1.x" + jxLoader "*" + moo-server "*" + promised-io "*" + timespan "2.x" + uglify-js "1.x" + walker "1.x" + winston "*" + wrench "1.3.x" + +byline@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" camelcase@^4.1.0: version "4.1.0" @@ -115,6 +327,39 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +cfb@~1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/cfb/-/cfb-1.0.8.tgz#77f213493d697d754fd9c0f5511eab5ad72d02cf" + dependencies: + commander "^2.14.1" + printj "~1.1.2" + +chai@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" + dependencies: + assertion-error "^1.0.1" + check-error "^1.0.1" + deep-eql "^3.0.0" + get-func-name "^2.0.0" + pathval "^1.0.0" + type-detect "^4.0.0" + +chalk@2.4.2, chalk@~2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -125,6 +370,19 @@ chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +check-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -137,63 +395,155 @@ cli-table@^0.3.1: dependencies: colors "1.0.3" +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" +codepage@~1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/codepage/-/codepage-1.14.0.tgz#8cbe25481323559d7d307571b0fff91e7a1d2f99" + dependencies: + commander "~2.14.1" + exit-on-epipe "~1.0.1" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + colors@1.0.3, colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" colors@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + version "1.2.5" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" +colors@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.1.tgz#4accdb89cf2cabc7f982771925e9468784f32f3d" + +combined-stream@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: delayed-stream "~1.0.0" -commander@^2.2.0, commander@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" +combined-stream@^1.0.6, combined-stream@~1.0.5, combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" dependencies: - graceful-readlink ">= 1.0.0" + delayed-stream "~1.0.0" + +commander@2.15.1, commander@^2.2.0, commander@^2.9.0, commander@~2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + +commander@^2.14.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + +commander@kriskbx/commander.js: + version "2.19.0" + resolved "https://codeload.github.com/kriskbx/commander.js/tar.gz/2275da95973c27e40e13a4195768947aa39672d0" + +commander@~2.14.1: + version "2.14.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" +concat-stream@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" concat-stream@^1.4.7: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: + buffer-from "^1.0.0" inherits "^2.0.3" readable-stream "^2.2.2" typedarray "^0.0.6" -core-util-is@~1.0.0: +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js@^2.4.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.3.tgz#4b70938bdffdaf64931e66e2db158f0892289c49" + +core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +coveralls@^2.13.1: + version "2.13.3" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.3.tgz#9ad7c2ae527417f361e8b626483f48ee92dd2bc7" + dependencies: + js-yaml "3.6.1" + lcov-parse "0.0.10" + log-driver "1.2.5" + minimist "1.2.0" + request "2.79.0" + +crc-32@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" dependencies: boom "2.x.x" +cryptiles@3.x.x: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.4.tgz#769a68c95612b56faadfcebf57ac86479cbe8322" + dependencies: + boom "5.x.x" + +cssmin@0.3.x: + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssmin/-/cssmin-0.3.2.tgz#ddce4c547b510ae0d594a8f1fbf8aaf8e2c5c00d" + csv-string@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/csv-string/-/csv-string-2.3.2.tgz#7ee58bd957a04de3f9935b7ea1ab29180156894c" + version "2.3.3" + resolved "https://registry.yarnpkg.com/csv-string/-/csv-string-2.3.3.tgz#e0801e3c6f92f0983629505774af66dd3090e9e5" cycle@1.0.x: version "1.0.3" @@ -205,73 +555,256 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-eql@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" deep-equal@~0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d" +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" +diff@3.5.0, diff@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" dependencies: jsbn "~0.1.0" + safer-buffer "^2.1.0" -es6-promise@~4.0.3: - version "4.0.5" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" +es6-promise@^4.0.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" -escape-string-regexp@^1.0.2: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -esprima@^3.1.1: +escodegen@1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-template@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.1.tgz#981f188c0c3a87d2e28f559bc541426ff94f21dd" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" dependencies: is-extendable "^0.1.0" +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@^3.0.0, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -extract-zip@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4" +extend@~3.0.1, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-zip@^1.6.5: + version "1.6.6" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" dependencies: - concat-stream "1.5.0" - debug "0.7.4" + concat-stream "1.6.0" + debug "2.6.9" mkdirp "0.5.0" yauzl "2.4.1" -extsprintf@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" eyes@0.1.x: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-glob@^2.0.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" dependencies: pend "~1.2.0" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + find-in-files@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/find-in-files/-/find-in-files-0.4.0.tgz#9cd4b67a3459c54381f56ce2f1d69316ad7bb011" @@ -285,6 +818,10 @@ find@^0.1.5: dependencies: traverse-chain "~0.1.0" +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -297,7 +834,46 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -fs-extra@~1.0.0: +form-data@~2.3.1, form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formatio@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.2.0.tgz#f3b2167d9068c4698a8d51f4f760a39a54d818eb" + dependencies: + samsam "1.x" + +frac@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" dependencies: @@ -305,6 +881,14 @@ fs-extra@~1.0.0: jsonfile "^2.1.0" klaw "^1.0.0" +fs-extra@~6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -319,34 +903,100 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" dependencies: assert-plus "^1.0.0" -glob@^7.0.5: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + +glob@7.1.2, glob@^7.0.5: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +glob@^7.0.0, glob@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globby@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + +graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +handlebars@^4.0.1: + version "4.0.11" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" har-validator@~2.0.6: version "2.0.6" @@ -357,12 +1007,19 @@ har-validator@~2.0.6: is-my-json-valid "^2.12.4" pinkie-promise "^2.0.0" -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" + ajv "^6.5.5" + har-schema "^2.0.0" has-ansi@^2.0.0: version "2.0.0" @@ -370,11 +1027,46 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + hash-sum@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" -hasha@~2.2.0: +hasha@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" dependencies: @@ -382,8 +1074,8 @@ hasha@~2.2.0: pinkie-promise "^2.0.0" hashids@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/hashids/-/hashids-1.1.1.tgz#3c36fcc5b3ba1a96a8fa67a632eb7877c41c6d3e" + version "1.1.4" + resolved "https://registry.yarnpkg.com/hashids/-/hashids-1.1.4.tgz#e4ff92ad66b684a3bd6aace7c17d66618ee5fa21" hawk@~3.1.3: version "3.1.3" @@ -394,6 +1086,19 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + highlight.js@^9.1.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" @@ -402,6 +1107,10 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" +hoek@4.x.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -410,9 +1119,25 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + i@0.3.x: - version "0.3.5" - resolved "https://registry.yarnpkg.com/i/-/i-0.3.5.tgz#1d2b854158ec8169113c6cb7f6b6801e99e211d5" + version "0.3.6" + resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + +in-publish@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" inflight@^1.0.4: version "1.0.6" @@ -421,23 +1146,118 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@~2.0.1: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -is-extendable@^0.1.0: +interpret@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" + integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== + +into-stream@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-4.0.0.tgz#ef10ee2ffb6f78af34c93194bbdc36c35f7d8a9d" + dependencies: + from2 "^2.1.1" + p-is-promise "^2.0.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + is-my-json-valid@^2.12.4: - version "2.16.0" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" jsonpointer "^4.0.0" xtend "^4.0.0" +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" @@ -450,7 +1270,15 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -isarray@~1.0.0: +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -458,37 +1286,77 @@ isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" +istanbul@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +js-yaml@0.3.x: + version "0.3.7" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-0.3.7.tgz#d739d8ee86461e54b354d6a7d7d1f2ad9a167f62" + +js-yaml@3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" dependencies: - jsbn "~0.1.0" + argparse "^1.0.7" + esprima "^2.6.0" -js-yaml@^3.8.2: - version "3.8.3" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" +js-yaml@3.x, js-yaml@^3.8.2: + version "3.11.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" dependencies: argparse "^1.0.7" - esprima "^3.1.1" + esprima "^4.0.0" jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" +jsmin@1.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jsmin/-/jsmin-1.0.1.tgz#e7bd0dcd6496c3bf4863235bf461a3d98aa3b98c" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -499,40 +1367,122 @@ jsonfile@^2.1.0: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" jsprim@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" dependencies: assert-plus "1.0.0" - extsprintf "1.0.2" + extsprintf "1.3.0" json-schema "0.2.3" - verror "1.3.6" + verror "1.10.0" -kew@~0.7.0: +just-extend@^1.1.27: + version "1.1.27" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905" + +jxLoader@*: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jxLoader/-/jxLoader-0.1.1.tgz#0134ea5144e533b594fc1ff25ff194e235c53ecd" + dependencies: + js-yaml "0.3.x" + moo-server "1.3.x" + promised-io "*" + walker "1.x" + +kew@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" optionalDependencies: graceful-fs "^4.1.9" -lodash@^4.13.1, lodash@^4.14.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcov-parse@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + +lodash@^4.13.1, lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +log-driver@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" -markdown-pdf@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/markdown-pdf/-/markdown-pdf-8.0.0.tgz#ae2b4b87162d7c3a98130312deac4aafffa15ca5" +lolex@^2.1.2, lolex@^2.3.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.4.2.tgz#d1d8df28fac51920b969aea10f90366b4c0d5e93" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +markdown-pdf@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/markdown-pdf/-/markdown-pdf-9.0.0.tgz#d699f29c3b6c41da4b9a2ec7d09ea8895daef146" dependencies: commander "^2.2.0" duplexer "^0.1.1" @@ -542,27 +1492,59 @@ markdown-pdf@^8.0.0: remarkable "^1.7.1" stream-from-to "^1.4.2" through2 "^2.0.0" - tmp "0.0.31" + tmp "0.0.33" markdown-table@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.0.tgz#1f5ae61659ced8808d882554c32e8b3f38dd1143" - -mime-db@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" - -mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.15" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" - dependencies: - mime-db "~1.27.0" + version "1.1.2" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" + +merge2@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" + +micromatch@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19: + version "2.1.21" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" + dependencies: + mime-db "~1.37.0" + +mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" mimic-fn@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" -minimatch@^3.0.2: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -572,41 +1554,161 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" +minimist@1.2.0, minimist@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" dependencies: minimist "0.0.8" -mkdirp@0.x.x, mkdirp@^0.5.0: +mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x.x, mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -moment@^2.18.1: - version "2.18.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" +mocha-lcov-reporter@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz#469bdef4f8afc9a116056f079df6182d0afb0384" + +mocha@^5: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +moment-timezone@^0.5.21: + version "0.5.21" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.21.tgz#3cba247d84492174dbf71de2a9848fa13207b845" + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0": + version "2.22.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad" + +moment@^2.22.2: + version "2.22.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" + +moo-server@*, moo-server@1.3.x: + version "1.3.0" + resolved "https://registry.yarnpkg.com/moo-server/-/moo-server-1.3.0.tgz#5dc79569565a10d6efed5439491e69d2392e58f1" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multistream@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/multistream/-/multistream-2.1.1.tgz#629d3a29bd76623489980d04519a2c365948148c" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.5" mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +native-promise-only@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/native-promise-only/-/native-promise-only-0.8.1.tgz#20a318c30cb45f71fe7adfbf7b21c99c1472ef11" + ncp@1.0.x: version "1.0.1" resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" +nise@^1.0.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.3.3.tgz#c17a850066a8a1dfeb37f921da02441afc4a82ba" + dependencies: + "@sinonjs/formatio" "^2.0.0" + just-extend "^1.1.27" + lolex "^2.3.2" + path-to-regexp "^1.7.0" + text-encoding "^0.6.4" + node-spinner@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/node-spinner/-/node-spinner-0.0.4.tgz#4c5dad762f953bdcae74ec000f6cea054ef20c8e" dependencies: util-extend "~1.0.1" -oauth-sign@~0.8.1: +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -once@^1.3.0: +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +once@1.x, once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -622,35 +1724,89 @@ open@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" -os-tmpdir@~1.0.1: +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +p-is-promise@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-dirname@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +path-parse@^1.0.5, path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +pathval@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" phantomjs-prebuilt@^2.1.3: - version "2.1.14" - resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0" - dependencies: - es6-promise "~4.0.3" - extract-zip "~1.5.0" - fs-extra "~1.0.0" - hasha "~2.2.0" - kew "~0.7.0" - progress "~1.1.8" - request "~2.79.0" - request-progress "~2.0.1" - which "~1.2.10" + version "2.1.16" + resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz#efd212a4a3966d3647684ea8ba788549be2aefef" + dependencies: + es6-promise "^4.0.3" + extract-zip "^1.6.5" + fs-extra "^1.0.0" + hasha "^2.2.0" + kew "^0.7.0" + progress "^1.1.8" + request "^2.81.0" + request-progress "^2.0.1" + which "^1.2.10" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" pinkie-promise@^2.0.0: version "2.0.1" @@ -662,26 +1818,81 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pkg-fetch@2.5.7: + version "2.5.7" + resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-2.5.7.tgz#f253fce31eb62d40547af0761cf824e0751bff09" + dependencies: + babel-runtime "~6.26.0" + byline "~5.0.0" + chalk "~2.4.1" + expand-template "~1.1.1" + fs-extra "~6.0.1" + in-publish "~2.0.0" + minimist "~1.2.0" + progress "~2.0.0" + request "~2.88.0" + request-progress "~3.0.0" + semver "~5.6.0" + unique-temp-dir "~1.0.0" + +pkg@^4.3.4: + version "4.3.7" + resolved "https://registry.yarnpkg.com/pkg/-/pkg-4.3.7.tgz#a80767283d728adbcdf668ad99250af440efd8e3" + dependencies: + "@babel/parser" "7.2.3" + babel-runtime "6.26.0" + chalk "2.4.2" + escodegen "1.11.0" + fs-extra "7.0.1" + globby "8.0.2" + into-stream "4.0.0" + minimist "1.2.0" + multistream "2.1.1" + pkg-fetch "2.5.7" + progress "2.0.3" + resolve "1.6.0" + stream-meter "1.0.4" + pkginfo@0.3.x: version "0.3.1" resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" pkginfo@0.x.x: - version "0.4.0" - resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.0.tgz#349dbb7ffd38081fcadc0853df687f0c7744cd65" + version "0.4.1" + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" -progress@^2.0.0: +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +printj@~1.1.0, printj@~1.1.2: + version "1.1.2" + resolved "http://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + +process-nextick-args@~2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +progress@2.0.3, progress@~2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" -progress@~1.1.8: +progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +promised-io@*: + version "0.3.5" + resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356" + prompt@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prompt/-/prompt-1.0.0.tgz#8e57123c396ab988897fb327fd3aedc3e735e4fe" @@ -693,21 +1904,29 @@ prompt@^1.0.0: utile "0.3.x" winston "2.1.x" +psl@^1.1.24: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + q@^1.0.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" qs@~6.3.0: version "6.3.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" +qs@~6.5.1, qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" read-yaml@^1.1.0: version "1.1.0" @@ -722,28 +1941,35 @@ read@1.0.x: dependencies: mute-stream "~0.0.4" -readable-stream@^2.1.5, readable-stream@^2.2.2: - version "2.2.9" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: - buffer-shims "~1.0.0" core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" + resolve "^1.1.6" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" remarkable@^1.7.1: version "1.7.1" @@ -752,12 +1978,26 @@ remarkable@^1.7.1: argparse "~0.1.15" autolinker "~0.15.0" -request-progress@~2.0.1: +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request-progress@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" dependencies: throttleit "^1.0.0" +request-progress@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + dependencies: + throttleit "^1.0.0" + request-promise-core@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" @@ -765,25 +2005,25 @@ request-promise-core@1.1.1: lodash "^4.13.1" request-promise-native@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.4.tgz#86988ec8eee408e45579fce83bfd05b3adf9a155" + version "1.0.5" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" dependencies: request-promise-core "1.1.1" stealthy-require "^1.1.0" - tough-cookie ">=2.3.0" + tough-cookie ">=2.3.3" -request@^2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" +request@2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" - caseless "~0.12.0" + caseless "~0.11.0" combined-stream "~1.0.5" extend "~3.0.0" forever-agent "~0.6.1" form-data "~2.1.1" - har-validator "~4.2.1" + har-validator "~2.0.6" hawk "~3.1.3" http-signature "~1.1.0" is-typedarray "~1.0.0" @@ -791,38 +2031,109 @@ request@^2.81.0: json-stringify-safe "~5.0.1" mime-types "~2.1.7" oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" + qs "~6.3.0" stringstream "~0.0.4" tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" + tunnel-agent "~0.4.1" uuid "^3.0.0" -request@~2.79.0: - version "2.79.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" +request@^2.81.0: + version "2.85.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.11.0" + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" combined-stream "~1.0.5" - extend "~3.0.0" + extend "~3.0.1" forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~2.0.6" - hawk "~3.1.3" - http-signature "~1.1.0" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - qs "~6.3.0" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" - uuid "^3.0.0" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@^2.87.0: + version "2.87.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@~2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.6.0.tgz#0fbd21278b27b4004481c395349e7aba60a9ff5c" + dependencies: + path-parse "^1.0.5" + +resolve@^1.1.6: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + dependencies: + path-parse "^1.0.6" restore-cursor@^2.0.0: version "2.0.0" @@ -831,56 +2142,216 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + revalidator@0.1.x: version "0.1.8" resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + rimraf@2.x.x: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" -safe-buffer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +samsam@1.3.0, samsam@1.x, samsam@^1.1.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" + +semver@~5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" series-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/series-stream/-/series-stream-1.0.1.tgz#311a09c5c1d5a091440832e1a480a47400f1005d" +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shelljs@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" + integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" +sinon@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-3.3.0.tgz#9132111b4bbe13c749c2848210864250165069b1" + dependencies: + build "^0.1.4" + diff "^3.1.0" + formatio "1.2.0" + lodash.get "^4.4.2" + lolex "^2.1.2" + native-promise-only "^0.8.1" + nise "^1.0.1" + path-to-regexp "^1.7.0" + samsam "^1.1.3" + text-encoding "0.6.4" + type-detect "^4.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" dependencies: hoek "2.x.x" +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +ssf@~0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/ssf/-/ssf-0.10.2.tgz#65b2b4fcdfd967bc8e8383a41349009893115976" + dependencies: + frac "~1.1.2" + sshpk@^1.7.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" + getpass "^0.1.1" jsbn "~0.1.0" + safer-buffer "^2.0.2" tweetnacl "~0.14.0" stack-trace@0.0.x: - version "0.0.9" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" stealthy-require@^1.1.0: version "1.1.1" @@ -895,30 +2366,74 @@ stream-from-to@^1.4.2: mkdirp "^0.5.0" series-stream "^1.0.1" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" +stream-meter@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/stream-meter/-/stream-meter-1.0.4.tgz#52af95aa5ea760a2491716704dbff90f73afdd1d" + dependencies: + readable-stream "^2.1.4" -string_decoder@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.1.tgz#62e200f039955a6810d8df0a33ffc0f013662d98" +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: - safe-buffer "^5.0.1" + safe-buffer "~5.1.0" stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +stringstream@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" dependencies: ansi-regex "^2.0.0" +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + +tempfile@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-2.0.0.tgz#6b0446856a9b1114d1856ffcbe509cccb0977265" + dependencies: + temp-dir "^1.0.0" + uuid "^3.0.1" + +text-encoding@0.6.4, text-encoding@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" + +throttled-queue@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/throttled-queue/-/throttled-queue-1.0.7.tgz#da7ed6702941993044a1c5fd2ac3a58582dd2977" + integrity sha512-/HT49S7m+NvdyJMoMRzIYlawKjeHn8jEc8TZaGmFi5IBu09hIiU/QoP1zcrB9X2qsVC11PgfRfqd8zEQs7PlEA== + throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" @@ -930,16 +2445,53 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" +timespan@2.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: - os-tmpdir "~1.0.1" + os-tmpdir "~1.0.2" -tough-cookie@>=2.3.0, tough-cookie@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +tough-cookie@>=2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" dependencies: + psl "^1.1.24" punycode "^1.4.1" traverse-chain@~0.1.0: @@ -960,22 +2512,95 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" -typedarray@^0.0.6, typedarray@~0.0.5: +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + +typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +uglify-js@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-1.3.5.tgz#4b5bfff9186effbaa888e4c9e94bd9fc4c94929d" + +uglify-js@^2.6: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid2@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" + underscore.string@~2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b" -underscore@^1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +underscore@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unique-temp-dir@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz#6dce95b2681ca003eebfb304a415f9cbabcc5385" + dependencies: + mkdirp "^0.5.1" + os-tmpdir "^1.0.1" + uid2 "0.0.3" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -995,22 +2620,49 @@ utile@0.3.x: ncp "1.0.x" rimraf "2.x.x" -uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" +uuid@^3.0.0, uuid@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +uuid@^3.1.0, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" -verror@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" dependencies: - extsprintf "1.0.2" + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +walker@1.x: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" -which@~1.2.10: - version "1.2.14" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" +which@^1.1.1, which@^1.2.10: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: isexe "^2.0.0" +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +winston@*: + version "2.4.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.2.tgz#3ca01f763116fc48db61053b7544e750431f8db0" + dependencies: + async "~1.0.0" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + stack-trace "0.0.x" + winston@2.1.x: version "2.1.1" resolved "https://registry.yarnpkg.com/winston/-/winston-2.1.1.tgz#3c9349d196207fd1bdff9d4bc43ef72510e3a12e" @@ -1023,14 +2675,55 @@ winston@2.1.x: pkginfo "0.3.x" stack-trace "0.0.x" +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@^1.0.0, wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +wrench@1.3.x: + version "1.3.9" + resolved "https://registry.yarnpkg.com/wrench/-/wrench-1.3.9.tgz#6f13ec35145317eb292ca5f6531391b244111411" + +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + +xlsx@^0.13.5: + version "0.13.5" + resolved "https://registry.yarnpkg.com/xlsx/-/xlsx-0.13.5.tgz#65826ba1944c6f4d6c023b7eb6c89e6b6e0d71b7" + dependencies: + adler-32 "~1.2.0" + cfb "~1.0.8" + codepage "~1.14.0" + commander "~2.15.1" + crc-32 "~1.2.0" + exit-on-epipe "~1.0.1" + ssf "~0.10.2" + xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + yauzl@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"