├── .eslintignore ├── .eslintrc.js ├── .github └── dependabot.yml ├── .gitignore ├── .sonarcloud.properties ├── .travis.yml ├── DEVELOPERS.md ├── GUIDE.md ├── LICENSE ├── README.md ├── RELEASES.md ├── ROADMAP.md ├── WEBTASK.md ├── bin └── pocket-cli ├── build.sh ├── jest.ci.js ├── jest.config.js ├── package.json ├── screens └── screen1.png ├── server ├── logger.js ├── package-lock.json ├── package.json ├── pocket-cli-auth-proxy │ ├── .gitignore │ ├── handler.js │ └── serverless.yml ├── pocket-http.js └── pocket-proxy-server.js ├── snap └── snapcraft.yaml ├── src ├── arrays-utils.js ├── auth.js ├── cli │ ├── cli.js │ ├── completer.js │ ├── guide.js │ ├── help.js │ ├── interpreter.js │ ├── loader.js │ ├── menu.js │ ├── open.js │ ├── process-input.js │ ├── quit.js │ ├── update.js │ └── version.js ├── commons-execute.js ├── commons-formatter.js ├── highland-utils.js ├── history.js ├── index.js ├── local-articles.js ├── npm-update.js ├── pocket │ ├── article-formatter.js │ ├── articles-formatter.js │ ├── pocket-auth.js │ ├── pocket-commands.js │ ├── pocket-execute.js │ ├── pocket-parse.js │ ├── pocket-sdk.js │ └── wc.js └── templates.js ├── test ├── article-formatter.test.js ├── auth.test.js ├── cli.test.js ├── commands.test.js ├── completer.test.js ├── fixtures │ ├── article-sample.js │ ├── article.js │ └── mock.parsed.articles.js ├── history.test.js ├── integrations │ ├── pocket-sdk.test.js │ ├── smoke.test.js │ └── update.test.js ├── interpreter-pocket-list-subcommands.test.js ├── interpreter-pocket-list.test.js ├── interpreter.test.js ├── intersections.test.js ├── load-templates-from-file.test.js ├── menu.test.js ├── open.test.js ├── pocket-actions.test.js ├── pocket-articles.test.js ├── pocket-auth-learning-test.js ├── pocket-list.test.js └── test-helpers.js └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'node-opinionated', 4 | 'plugin:jest/recommended' 5 | ], 6 | plugins: ['jest'], 7 | overrides: [ 8 | { 9 | files: ['**/*test*/**'], 10 | rules: { 11 | 'node/no-unpublished-require': 'off', 12 | 'node/no-unpublished-import': 'off', 13 | 'max-nested-callbacks': ['warn', 3], 14 | 'max-lines': ['warn', 200], 15 | 'sonarjs/no-duplicate-string': 'off', 16 | 'no-console': 'off', 17 | 'no-sync': 'off', 18 | 'no-undefined': 'off', 19 | 'security/detect-child-process': 'off', 20 | 'security/detect-non-literal-fs-filename': 'off', 21 | 'security/detect-non-literal-require': 'off' 22 | } 23 | }, 24 | { 25 | files: ['**/*src*/**'], 26 | rules: { 27 | 'no-console': 'off', 28 | 'no-process-exit': 'off', 29 | 'no-sync': 'off', // TBRemoved as we're not on Node12 30 | 'sonarjs/no-unused-collection': 'off', 31 | 'security/detect-child-process': 'off', 32 | } 33 | }, 34 | { 35 | files: ['**/**'], 36 | rules: { 37 | 'camelcase': 'off', 38 | } 39 | }, 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 5 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .*env 3 | .snapcraft 4 | pocket_access_token 5 | coverage 6 | parts 7 | *.snap 8 | prime 9 | stage 10 | index-lunr* 11 | yarn-error.log -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ildella/pocket-cli/fc040b863d2f75c89b24eeddf88aa6ab89068f92/.sonarcloud.properties -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - '12' 5 | script: 6 | - yarn depcheck 7 | - yarn lint 8 | - yarn test:ci 9 | deploy: 10 | provider: npm 11 | email: ildella@gmail.com 12 | api_key: 13 | secure: XvBht9Pm3lr/Oc0WY4a4z+Dbqj1JbymHzgX2bT/4FEBi3d4IEDmrpZdwMqLwuN1scGvEL2/UN6Hy5HZJbfvR0Bik5K5aFWSd+rs2/8U95CGI3jRBaDOORpMI+102VAStuP2lemezyJjgDK2kWPYHXe+WIUh6YNNA18SxHc3LQ9hgfe7K/c8XliLWMhgmHoBegDEkAHwvDvag59nARXFlV8Jhu03Cd/K/Z67L9EYs3Y/sN7SV9TsYdH+r4eYWVwGEUtWuLEVTcrlq4kw762E6C0DAO+jWJ0OLFv7Wy374rvWd9pqNkrJTOykSGuqFqdc1WkhWMLPzC6gCWQ+bEi2rsnB+Djp5ryI6lABXOd+fPH2opNtWFZvDNehPxegJqo1mNPwgLaPyevJrKC489fNSbbL/tWoqu3ymaqHUv8XKD90uioMTIJ0C4HahHL6t9x6sRcA/NprFuGbleobmcxRNayVl9sWpyFi2tGs8CyUh5yzFMiXz0rkMC/i6BKgHXA90woCy0MuQSQuDuz6oxX1qwjQ97iDMiomb2mCKkRynbdedHk3vZNyhQp/vQwNRJFx0ryQHjP+XSC1iIfUWUUlnGkpYslopdxTdj9vJJt05Plz/KumpFnp6ZExxKzto2vpHXWNo1xXXU4H8ZqK2YX2OBoyOEXMmHqQzpTe3dr3l41I= 14 | on: 15 | tags: true 16 | repo: ildella/pocket-cli 17 | skip_cleanup: 'true' 18 | -------------------------------------------------------------------------------- /DEVELOPERS.md: -------------------------------------------------------------------------------- 1 | # DEVELOPERS 2 | 3 | ## Prep the client 4 | 5 | ```bash 6 | git clone git://github.com/ildella/pocket-cli.git 7 | cd pocket-cli 8 | npm install 9 | ``` 10 | 11 | ## Prep the auth proxy 12 | 13 | In order to communicate with the Pocket API, we always need to send both the consumer key, unique to the pocket-cli app, and the user token, which is obtained trough a oauth exchange. 14 | 15 | We need to run a proxy that securely stores the uniquer pocket-cli consumer key to access the Pocket API. 16 | 17 | To try it in dev, create a fresh [API key from Pocket](https://getpocket.com/developer/apps/new) 18 | Store it in a file called .env in the project root, like this 19 | 20 | ```bash 21 | POCKET='' 22 | ``` 23 | 24 | The proxy server can be started locally: 25 | 26 | ```bash 27 | wt serve src/server/auth-proxy.js --port 4040 28 | ``` 29 | 30 | ## Run it 31 | 32 | ```bash 33 | ./bin/pocket-cli 34 | ``` 35 | 36 | ### Link 37 | 38 | To use locally the current branch: 39 | 40 | ```bash 41 | npm link 42 | ``` 43 | 44 | To link it to a different bin: 45 | 46 | ``` 47 | sudo ln -s "$HOME/n/bin/pocket-cli" /usr/local/bin/pocket-cli-dev 48 | ``` 49 | 50 | ## Depencencies. 51 | 52 | I have a quite strict policy on dependencies. 53 | 54 | ```bash 55 | $ yarn list --prod 56 | pocket-cli@0.4.2 /home/ildella/projects/personal/pocket-cli 57 | ├─┬ axios@0.18.0 58 | │ ├─┬ follow-redirects@1.5.9 59 | │ │ └─┬ debug@3.1.0 60 | │ │ └── ms@2.0.0 61 | │ └── is-buffer@1.1.6 62 | ├── colorette@1.0.7 63 | └── luxon@1.8.2 64 | ``` 65 | 66 | I do not use most common command line library or framework like commander, inquirer, chalk. I also do not use lodash or similar libraries. 67 | 68 | What I rely on: 69 | 70 | - Axios - http client 71 | - Colorette - a dependency free ansii coloring 72 | - Luxon - a dependency free, immutable momentjs 73 | 74 | ## Publish from master 75 | 76 | Docs: https://docs.travis-ci.com/user/deployment/npm/ 77 | 78 | Install travis locally: 79 | 80 | ```shell 81 | sudo apt install ruby ruby-dev 82 | sudo gem install travis --no-document 83 | ``` 84 | 85 | Generate a new [GitHub Access Token](https://github.com/settings/tokens) with the [scopes](https://docs.travis-ci.com/user/github-oauth-scopes/#travis-ci-for-open-source-projects) as specified in "Repositories on https://travis-ci.org" section. 86 | 87 | Now, authenticate to Travis and setup the project. This whole thing should be done only once. 88 | 89 | ```shell 90 | travis login --github-token GITHUB_ACCESS_TOKEN 91 | ``` 92 | 93 | Generate a new [NPM access token](https://www.npmjs.com/settings/ildella/tokens/) which will be used and encrypted in this interactive shell: 94 | 95 | ```shell 96 | travis setup npm 97 | ``` 98 | 99 | This will generate or upgrade the `.travis.yml` file in the project. The relevant portion is 100 | 101 | ```yaml 102 | deploy: 103 | provider: npm 104 | email: ildella@gmail.com 105 | api_key: 106 | secure: xxxxxxxxxxxxxx 107 | on: 108 | tags: true 109 | repo: ildella/pocket-cli 110 | skip_cleanup: 'true' 111 | ``` 112 | 113 | This will publish to npm each tag that we push to GitHub like this: 114 | 115 | ```shell 116 | git tag v0.x.x 117 | git push origin v0.x.x 118 | ``` 119 | 120 | ## Snap 121 | 122 | To release as a snap installer: 123 | 124 | ```shell 125 | sudo apt install snapcraft 126 | snapcraft 127 | ``` 128 | -------------------------------------------------------------------------------- /GUIDE.md: -------------------------------------------------------------------------------- 1 | # GUIDE 2 | 3 | ## Commands 4 | 5 | The "help" will show all available commands with a description. 6 | Type "help" or "?" to show help 7 | Type "? [command]" to show the command detailed help (eg: ? list) 8 | 9 | ## Search 10 | 11 | We assume the default action is search, so any input that is not a Command or a Reserved Word, will be used to search. This means that: 12 | 13 | `list bitcoin` 14 | will have the same effect as 15 | `bitcoin` 16 | 17 | ### Search - Reserved Words 18 | 19 | unread: show only non-archived 20 | oldest / newest: list starting from oldest / newest 21 | 22 | ## Actions 23 | 24 | When you have a list, you can apply actions to the articles. 25 | Each article has an index from 1 to 8. So we do: 26 | 27 | archive article 1, 2 and 3 from the last search 28 | `archive 1 2 3` 29 | shortcut for the above command 30 | `a 1 2 3` 31 | open selected indexes in browser 32 | `o 1 2 3` 33 | 34 | ## Interactive Mode 35 | 36 | The alternative way to use actions is selecting the index first, then selecting the action from the menu. 37 | 38 | The default action is "open", so that: 39 | 40 | Quick open 41 | `2 ` 42 | 43 | ## Support commands 44 | 45 | guide 46 | update 47 | update whatsnew 48 | help -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2019 Daniele Dellafiore 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pocket CLI 2 | 3 | Pocket-CLI is an interactive textual application to search and manage your [Pocket](https://getpocket.com) articles from the terminal. 4 | 5 |

6 | pocket-cli 7 |

8 | 9 | [![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 10 | [![Version](https://img.shields.io/npm/v/pocket-cli.svg?style=flat-square)](https://npmjs.com/package/pocket-cli) 11 | [![Build Status](https://travis-ci.com/ildella/pocket-cli.svg?branch=master)](https://travis-ci.com/ildella/pocket-cli) 12 | [![Known Vulnerabilities](https://snyk.io/test/github/ildella/pocket-cli/badge.svg?targetFile=package.json)](https://snyk.io/test/github/ildella/pocket-cli?targetFile=package.json) 13 | [![David](https://img.shields.io/david/ildella/pocket-cli.svg)](https://david-dm.org/ildella/pocket-cli) 14 | [![David](https://img.shields.io/david/dev/ildella/pocket-cli.svg)](https://david-dm.org/ildella/pocket-cli) 15 | [![David](https://img.shields.io/david/peer/ildella/pocket-cli.svg)](https://david-dm.org/ildella/pocket-cli) 16 | ## Install 17 | 18 | ### Prerequisites 19 | 20 | You must have [Node.js](https://nodejs.org/) 21 | 22 | In Ubuntu: 23 | 24 | ```bash 25 | sudo snap install node --channel=12 26 | ``` 27 | 28 | ### Install with NPM 29 | 30 | ```bash 31 | npm i -g pocket-cli 32 | ``` 33 | 34 | ### Install with Snap 35 | 36 | TBD 37 | 38 | ## Run 39 | 40 | Some example command 41 | 42 | ```shell 43 | $ pocket-cli 44 | 45 | Pocket> login // Authenticate with Pocket to get access to your articles 46 | Pocket> ? // prints help 47 | Pocket> list // last 8 articles 48 | Pocket> unread // last 8 unread articles 49 | Pocket> bitcoin // search for articles with 'bitcoin' 50 | Pocket> archive 1 2 3 // archive article 1, 2 and 3 from the last search 51 | Pocket> a 1 2 3 // shortcut for archive 52 | Pocket> o 1 2 3 // open selected indexes in browser 53 | Pocket> 2 // open second result (press enter twice) 54 | Pocket> update whatsnew // prints release notes 55 | ``` 56 | 57 | Use TAB to autocomplete 58 | 59 | ## Contribute 60 | 61 | [For developers](DEVELOPERS.md) 62 | 63 | ## Author 64 | 65 | Built by Daniele Dellafiore. Get in touch [on Twitter](https://twitter.com/ildella) 66 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | ## 0.8.2 -> 17 Oct, 2019 2 | 3 | * upgraded all dependencies to latest version to avoid security vulterabilities 4 | * upgraded official Node version to 12 5 | 6 | ## 0.8.1 -> Jan 5th, 2019 7 | 8 | * new: Fav article are marked with a * 9 | * new: list will show the tags of each item 10 | * new: "tag 1 myTag" will tag. Is not positional, so "tag myTag 1 2" is same as "tag 1 myTag 2". Is semantic. Fake, but semantic :) 11 | * improved: When an index is selected, action 4 is now Archive or Readd according to the article status 12 | * improved: command completion has better behavior 13 | * change: index and title no longer have bright colors 14 | * fixed: previous command is broken in 0.7.9 15 | * under the hood: Large refactoring. Tests % are 80.13 | 62.7 | 67.83 | 80.09 16 | * prep code for offline storage (download.js) and index (build-index.js) 17 | 18 | ## 0.7.9 -> Dec 23, 2018 19 | 20 | * fix/loader: the loader symbol does not disappear after search is finished 21 | * help text after search is in gray + typo fix 22 | 23 | ## 0.7.8 -> Dec 21, 2018 24 | 25 | * More detailed help and a new 'guide' command 26 | * Fix: action time is wrong, this causes'readd' command to malfunction 27 | * Improved UI: show clear "no results found" message whe... appropriate 28 | * Improved UI: more clear search and actions messages in results screen 29 | 30 | ## 0.7.3 -> Dec 15, 2018 31 | 32 | * show (A) before archived articles title 33 | * update now print the command to run update + whatsnew subcommand shows RELEASES 34 | * Open supports multiple indexes 35 | 36 | ## 0.7.0 -> Dec 14, 2018 37 | 38 | * Login with proper user flow for first usage 39 | * Logout 40 | * Update: Check for updates 41 | 42 | ## 0.6.0 - Dec 13, 2018 43 | 44 | * More clear UI for non authenticated users 45 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # ROADMAP 2 | 3 | ## 0.9 4 | 5 | * refactor the cli/processor/interpreter in order to add new features like permanent filters, permanent custom prompt 6 | * complete support for tag command 7 | * the refactor will help moving forward offline mode 8 | -------------------------------------------------------------------------------- /WEBTASK.md: -------------------------------------------------------------------------------- 1 | # WEBTASK 2 | 3 | The proxy server runs on Webtask 4 | 5 | ## Create and update commands 6 | 7 | ```bash 8 | cd src/server 9 | npm run create 10 | npm run update:watch 11 | ``` 12 | 13 | ## Custom domain 14 | 15 | In order to verify the host we need to perform 2 actions on the DNS 16 | 17 | 1. CNAME pocketcli -> sandbox.auth0-extend.com 18 | 2. TXT `webtask:container:wt-c7bbe7e68d36c0caa6436b2be9c7052a-0` to the DNS of the pocketcli.pipelean.com domain. 19 | -------------------------------------------------------------------------------- /bin/pocket-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../src').init() 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm "$HOME/n/lib/node_modules/pocket-cli" 3 | yarn run snap -------------------------------------------------------------------------------- /jest.ci.js: -------------------------------------------------------------------------------- 1 | const defaultJestConfig = require('./jest.config') 2 | module.exports = { 3 | ...defaultJestConfig, 4 | collectCoverage: true, 5 | // TODO: super-shame... 6 | coverageThreshold: { 7 | global: {branches: 30, functions: 30, lines: 30, statements: 30} 8 | }, 9 | 10 | } 11 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: false, 3 | notify: false, 4 | notifyMode: 'failure-change, success-change', 5 | testEnvironment: 'node', 6 | testMatch: ['**/test*/*.test.js'], 7 | testPathIgnorePatterns: ['/node_modules'], 8 | // setupFilesAfterEnv: ['./tests/test-helpers.js'], 9 | collectCoverage: false, 10 | collectCoverageFrom: [ 11 | '**/src/*.{js,jsx}', 12 | '!**/node_modules/**', 13 | ], 14 | coverageReporters: ['text', 'text-summary', 'json', 'json-summary', 'lcov', 'clover', 'html'], 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pocket-cli", 3 | "version": "0.8.5", 4 | "description": "Interactive terminal application for Pocket", 5 | "bin": { 6 | "pocket-cli": "bin/pocket-cli" 7 | }, 8 | "engines": { 9 | "node": ">=12" 10 | }, 11 | "homepage": "https://github.com/ildella/pocket-cli#readme", 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ildella/pocket-cli.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/ildella/pocket-cli/issues" 18 | }, 19 | "keywords": [ 20 | "pocket", 21 | "readitlater", 22 | "cli", 23 | "interactive", 24 | "shell" 25 | ], 26 | "author": { 27 | "name": "Daniele Dellafiore", 28 | "email": "daniele@dellafiore.net" 29 | }, 30 | "license": "MIT", 31 | "dependencies": { 32 | "axios": "0.19.2", 33 | "colorette": "1.2.0", 34 | "highland": "2.13.5", 35 | "luxon": "1.24.1", 36 | "semver": "7.3.2" 37 | }, 38 | "devDependencies": { 39 | "chai": "4.2.0", 40 | "depcheck": "0.9.2", 41 | "dotenv": "8.2.0", 42 | "eslint": "7.16.0", 43 | "eslint-config-node-opinionated": "0.4.0", 44 | "eslint-plugin-jest": "23.13.2", 45 | "eslint-plugin-node": "11.1.0", 46 | "eslint-plugin-security": "1.4.0", 47 | "eslint-plugin-sonarjs": "0.5.0", 48 | "jest": "26.0.1" 49 | }, 50 | "scripts": { 51 | "depcheck": "depcheck .", 52 | "start": "node .", 53 | "test": "jest", 54 | "test:ci": "jest --config=jest.ci.js", 55 | "lint": "eslint src/ test/ --max-warnings 71", 56 | "snap": "snapcraft", 57 | "snap:help": "snapcraft help nodejs", 58 | "snap:clean": "snapcraft clean pocket-cli -s pull" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /screens/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ildella/pocket-cli/fc040b863d2f75c89b24eeddf88aa6ab89068f92/screens/screen1.png -------------------------------------------------------------------------------- /server/logger.js: -------------------------------------------------------------------------------- 1 | module.exports = logLevel => { 2 | return require('tracer').colorConsole({ 3 | level: logLevel || 'info', 4 | dateformat: 'HH:MM:ss', 5 | transport: [ 6 | data => { console.log(data.output) } 7 | ] 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pocket-proxy-server", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.5.5", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", 10 | "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", 11 | "requires": { 12 | "@babel/highlight": "^7.0.0" 13 | } 14 | }, 15 | "@babel/generator": { 16 | "version": "7.7.2", 17 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", 18 | "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", 19 | "requires": { 20 | "@babel/types": "^7.7.2", 21 | "jsesc": "^2.5.1", 22 | "lodash": "^4.17.13", 23 | "source-map": "^0.5.0" 24 | } 25 | }, 26 | "@babel/helper-function-name": { 27 | "version": "7.7.0", 28 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", 29 | "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", 30 | "requires": { 31 | "@babel/helper-get-function-arity": "^7.7.0", 32 | "@babel/template": "^7.7.0", 33 | "@babel/types": "^7.7.0" 34 | } 35 | }, 36 | "@babel/helper-get-function-arity": { 37 | "version": "7.7.0", 38 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", 39 | "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", 40 | "requires": { 41 | "@babel/types": "^7.7.0" 42 | } 43 | }, 44 | "@babel/helper-split-export-declaration": { 45 | "version": "7.7.0", 46 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", 47 | "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", 48 | "requires": { 49 | "@babel/types": "^7.7.0" 50 | } 51 | }, 52 | "@babel/highlight": { 53 | "version": "7.5.0", 54 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", 55 | "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", 56 | "requires": { 57 | "chalk": "^2.0.0", 58 | "esutils": "^2.0.2", 59 | "js-tokens": "^4.0.0" 60 | } 61 | }, 62 | "@babel/parser": { 63 | "version": "7.7.3", 64 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", 65 | "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==" 66 | }, 67 | "@babel/template": { 68 | "version": "7.7.0", 69 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", 70 | "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", 71 | "requires": { 72 | "@babel/code-frame": "^7.0.0", 73 | "@babel/parser": "^7.7.0", 74 | "@babel/types": "^7.7.0" 75 | } 76 | }, 77 | "@babel/traverse": { 78 | "version": "7.7.2", 79 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", 80 | "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", 81 | "requires": { 82 | "@babel/code-frame": "^7.5.5", 83 | "@babel/generator": "^7.7.2", 84 | "@babel/helper-function-name": "^7.7.0", 85 | "@babel/helper-split-export-declaration": "^7.7.0", 86 | "@babel/parser": "^7.7.2", 87 | "@babel/types": "^7.7.2", 88 | "debug": "^4.1.0", 89 | "globals": "^11.1.0", 90 | "lodash": "^4.17.13" 91 | }, 92 | "dependencies": { 93 | "debug": { 94 | "version": "4.1.1", 95 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 96 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 97 | "requires": { 98 | "ms": "^2.1.1" 99 | } 100 | }, 101 | "ms": { 102 | "version": "2.1.2", 103 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 104 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 105 | } 106 | } 107 | }, 108 | "@babel/types": { 109 | "version": "7.7.2", 110 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", 111 | "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", 112 | "requires": { 113 | "esutils": "^2.0.2", 114 | "lodash": "^4.17.13", 115 | "to-fast-properties": "^2.0.0" 116 | } 117 | }, 118 | "@types/babel-types": { 119 | "version": "7.0.7", 120 | "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", 121 | "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==" 122 | }, 123 | "@types/babylon": { 124 | "version": "6.16.5", 125 | "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", 126 | "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", 127 | "requires": { 128 | "@types/babel-types": "*" 129 | } 130 | }, 131 | "accepts": { 132 | "version": "1.3.7", 133 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 134 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 135 | "requires": { 136 | "mime-types": "~2.1.24", 137 | "negotiator": "0.6.2" 138 | } 139 | }, 140 | "acorn": { 141 | "version": "3.3.0", 142 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 143 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" 144 | }, 145 | "acorn-globals": { 146 | "version": "3.1.0", 147 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", 148 | "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", 149 | "requires": { 150 | "acorn": "^4.0.4" 151 | }, 152 | "dependencies": { 153 | "acorn": { 154 | "version": "4.0.13", 155 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", 156 | "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" 157 | } 158 | } 159 | }, 160 | "align-text": { 161 | "version": "0.1.4", 162 | "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", 163 | "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", 164 | "requires": { 165 | "kind-of": "^3.0.2", 166 | "longest": "^1.0.1", 167 | "repeat-string": "^1.5.2" 168 | } 169 | }, 170 | "ansi-regex": { 171 | "version": "4.1.0", 172 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 173 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 174 | }, 175 | "ansi-styles": { 176 | "version": "3.2.1", 177 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 178 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 179 | "requires": { 180 | "color-convert": "^1.9.0" 181 | } 182 | }, 183 | "append-transform": { 184 | "version": "1.0.0", 185 | "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", 186 | "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", 187 | "requires": { 188 | "default-require-extensions": "^2.0.0" 189 | } 190 | }, 191 | "archy": { 192 | "version": "1.0.0", 193 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 194 | "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" 195 | }, 196 | "argparse": { 197 | "version": "1.0.10", 198 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 199 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 200 | "requires": { 201 | "sprintf-js": "~1.0.2" 202 | } 203 | }, 204 | "array-flatten": { 205 | "version": "1.1.1", 206 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 207 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 208 | }, 209 | "asap": { 210 | "version": "2.0.6", 211 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 212 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 213 | }, 214 | "asynckit": { 215 | "version": "0.4.0", 216 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 217 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 218 | }, 219 | "axios": { 220 | "version": "0.19.0", 221 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", 222 | "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", 223 | "requires": { 224 | "follow-redirects": "1.5.10", 225 | "is-buffer": "^2.0.2" 226 | } 227 | }, 228 | "babel-runtime": { 229 | "version": "6.26.0", 230 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 231 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 232 | "requires": { 233 | "core-js": "^2.4.0", 234 | "regenerator-runtime": "^0.11.0" 235 | } 236 | }, 237 | "babel-types": { 238 | "version": "6.26.0", 239 | "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", 240 | "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", 241 | "requires": { 242 | "babel-runtime": "^6.26.0", 243 | "esutils": "^2.0.2", 244 | "lodash": "^4.17.4", 245 | "to-fast-properties": "^1.0.3" 246 | }, 247 | "dependencies": { 248 | "to-fast-properties": { 249 | "version": "1.0.3", 250 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", 251 | "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" 252 | } 253 | } 254 | }, 255 | "babylon": { 256 | "version": "6.18.0", 257 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 258 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" 259 | }, 260 | "balanced-match": { 261 | "version": "1.0.0", 262 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 263 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 264 | }, 265 | "body-parser": { 266 | "version": "1.19.0", 267 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 268 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 269 | "requires": { 270 | "bytes": "3.1.0", 271 | "content-type": "~1.0.4", 272 | "debug": "2.6.9", 273 | "depd": "~1.1.2", 274 | "http-errors": "1.7.2", 275 | "iconv-lite": "0.4.24", 276 | "on-finished": "~2.3.0", 277 | "qs": "6.7.0", 278 | "raw-body": "2.4.0", 279 | "type-is": "~1.6.17" 280 | }, 281 | "dependencies": { 282 | "debug": { 283 | "version": "2.6.9", 284 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 285 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 286 | "requires": { 287 | "ms": "2.0.0" 288 | } 289 | } 290 | } 291 | }, 292 | "boom": { 293 | "version": "7.3.0", 294 | "resolved": "https://registry.npmjs.org/boom/-/boom-7.3.0.tgz", 295 | "integrity": "sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A==", 296 | "requires": { 297 | "hoek": "6.x.x" 298 | } 299 | }, 300 | "brace-expansion": { 301 | "version": "1.1.11", 302 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 303 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 304 | "requires": { 305 | "balanced-match": "^1.0.0", 306 | "concat-map": "0.0.1" 307 | } 308 | }, 309 | "buffer-equal-constant-time": { 310 | "version": "1.0.1", 311 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 312 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 313 | }, 314 | "bytes": { 315 | "version": "3.1.0", 316 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 317 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 318 | }, 319 | "caching-transform": { 320 | "version": "3.0.2", 321 | "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", 322 | "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", 323 | "requires": { 324 | "hasha": "^3.0.0", 325 | "make-dir": "^2.0.0", 326 | "package-hash": "^3.0.0", 327 | "write-file-atomic": "^2.4.2" 328 | } 329 | }, 330 | "camelcase": { 331 | "version": "5.3.1", 332 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 333 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 334 | }, 335 | "center-align": { 336 | "version": "0.1.3", 337 | "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", 338 | "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", 339 | "requires": { 340 | "align-text": "^0.1.3", 341 | "lazy-cache": "^1.0.3" 342 | } 343 | }, 344 | "chalk": { 345 | "version": "2.4.2", 346 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 347 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 348 | "requires": { 349 | "ansi-styles": "^3.2.1", 350 | "escape-string-regexp": "^1.0.5", 351 | "supports-color": "^5.3.0" 352 | } 353 | }, 354 | "character-parser": { 355 | "version": "2.2.0", 356 | "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", 357 | "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", 358 | "requires": { 359 | "is-regex": "^1.0.3" 360 | } 361 | }, 362 | "clean-css": { 363 | "version": "4.2.1", 364 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", 365 | "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", 366 | "requires": { 367 | "source-map": "~0.6.0" 368 | }, 369 | "dependencies": { 370 | "source-map": { 371 | "version": "0.6.1", 372 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 373 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 374 | } 375 | } 376 | }, 377 | "cliui": { 378 | "version": "5.0.0", 379 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 380 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 381 | "requires": { 382 | "string-width": "^3.1.0", 383 | "strip-ansi": "^5.2.0", 384 | "wrap-ansi": "^5.1.0" 385 | } 386 | }, 387 | "color-convert": { 388 | "version": "1.9.3", 389 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 390 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 391 | "requires": { 392 | "color-name": "1.1.3" 393 | } 394 | }, 395 | "color-name": { 396 | "version": "1.1.3", 397 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 398 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 399 | }, 400 | "colors": { 401 | "version": "1.3.3", 402 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", 403 | "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" 404 | }, 405 | "combined-stream": { 406 | "version": "1.0.8", 407 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 408 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 409 | "requires": { 410 | "delayed-stream": "~1.0.0" 411 | } 412 | }, 413 | "commander": { 414 | "version": "2.20.3", 415 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 416 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 417 | "optional": true 418 | }, 419 | "commondir": { 420 | "version": "1.0.1", 421 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 422 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" 423 | }, 424 | "component-emitter": { 425 | "version": "1.3.0", 426 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", 427 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" 428 | }, 429 | "concat-map": { 430 | "version": "0.0.1", 431 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 432 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 433 | }, 434 | "constantinople": { 435 | "version": "3.1.2", 436 | "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", 437 | "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", 438 | "requires": { 439 | "@types/babel-types": "^7.0.0", 440 | "@types/babylon": "^6.16.2", 441 | "babel-types": "^6.26.0", 442 | "babylon": "^6.18.0" 443 | } 444 | }, 445 | "content-disposition": { 446 | "version": "0.5.3", 447 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 448 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 449 | "requires": { 450 | "safe-buffer": "5.1.2" 451 | } 452 | }, 453 | "content-type": { 454 | "version": "1.0.4", 455 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 456 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 457 | }, 458 | "convert-source-map": { 459 | "version": "1.7.0", 460 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", 461 | "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", 462 | "requires": { 463 | "safe-buffer": "~5.1.1" 464 | } 465 | }, 466 | "cookie": { 467 | "version": "0.4.0", 468 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 469 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 470 | }, 471 | "cookie-signature": { 472 | "version": "1.0.6", 473 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 474 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 475 | }, 476 | "cookiejar": { 477 | "version": "2.1.2", 478 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", 479 | "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" 480 | }, 481 | "core-js": { 482 | "version": "2.6.10", 483 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", 484 | "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" 485 | }, 486 | "core-util-is": { 487 | "version": "1.0.2", 488 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 489 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 490 | }, 491 | "cp-file": { 492 | "version": "6.2.0", 493 | "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", 494 | "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", 495 | "requires": { 496 | "graceful-fs": "^4.1.2", 497 | "make-dir": "^2.0.0", 498 | "nested-error-stacks": "^2.0.0", 499 | "pify": "^4.0.1", 500 | "safe-buffer": "^5.0.1" 501 | } 502 | }, 503 | "cross-spawn": { 504 | "version": "4.0.2", 505 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", 506 | "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", 507 | "requires": { 508 | "lru-cache": "^4.0.1", 509 | "which": "^1.2.9" 510 | } 511 | }, 512 | "dateformat": { 513 | "version": "3.0.3", 514 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", 515 | "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" 516 | }, 517 | "debug": { 518 | "version": "3.1.0", 519 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 520 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 521 | "requires": { 522 | "ms": "2.0.0" 523 | } 524 | }, 525 | "decamelize": { 526 | "version": "1.2.0", 527 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 528 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 529 | }, 530 | "default-require-extensions": { 531 | "version": "2.0.0", 532 | "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", 533 | "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", 534 | "requires": { 535 | "strip-bom": "^3.0.0" 536 | } 537 | }, 538 | "delayed-stream": { 539 | "version": "1.0.0", 540 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 541 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 542 | }, 543 | "depd": { 544 | "version": "1.1.2", 545 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 546 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 547 | }, 548 | "destroy": { 549 | "version": "1.0.4", 550 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 551 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 552 | }, 553 | "doctypes": { 554 | "version": "1.1.0", 555 | "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", 556 | "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" 557 | }, 558 | "ecdsa-sig-formatter": { 559 | "version": "1.0.11", 560 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 561 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 562 | "requires": { 563 | "safe-buffer": "^5.0.1" 564 | } 565 | }, 566 | "ee-first": { 567 | "version": "1.1.1", 568 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 569 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 570 | }, 571 | "emoji-regex": { 572 | "version": "7.0.3", 573 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 574 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 575 | }, 576 | "encodeurl": { 577 | "version": "1.0.2", 578 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 579 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 580 | }, 581 | "error-ex": { 582 | "version": "1.3.2", 583 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 584 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 585 | "requires": { 586 | "is-arrayish": "^0.2.1" 587 | } 588 | }, 589 | "es6-error": { 590 | "version": "4.1.1", 591 | "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", 592 | "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" 593 | }, 594 | "escape-html": { 595 | "version": "1.0.3", 596 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 597 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 598 | }, 599 | "escape-string-regexp": { 600 | "version": "1.0.5", 601 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 602 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 603 | }, 604 | "esprima": { 605 | "version": "4.0.1", 606 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 607 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 608 | }, 609 | "esutils": { 610 | "version": "2.0.3", 611 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 612 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 613 | }, 614 | "etag": { 615 | "version": "1.8.1", 616 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 617 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 618 | }, 619 | "express": { 620 | "version": "4.17.1", 621 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 622 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 623 | "requires": { 624 | "accepts": "~1.3.7", 625 | "array-flatten": "1.1.1", 626 | "body-parser": "1.19.0", 627 | "content-disposition": "0.5.3", 628 | "content-type": "~1.0.4", 629 | "cookie": "0.4.0", 630 | "cookie-signature": "1.0.6", 631 | "debug": "2.6.9", 632 | "depd": "~1.1.2", 633 | "encodeurl": "~1.0.2", 634 | "escape-html": "~1.0.3", 635 | "etag": "~1.8.1", 636 | "finalhandler": "~1.1.2", 637 | "fresh": "0.5.2", 638 | "merge-descriptors": "1.0.1", 639 | "methods": "~1.1.2", 640 | "on-finished": "~2.3.0", 641 | "parseurl": "~1.3.3", 642 | "path-to-regexp": "0.1.7", 643 | "proxy-addr": "~2.0.5", 644 | "qs": "6.7.0", 645 | "range-parser": "~1.2.1", 646 | "safe-buffer": "5.1.2", 647 | "send": "0.17.1", 648 | "serve-static": "1.14.1", 649 | "setprototypeof": "1.1.1", 650 | "statuses": "~1.5.0", 651 | "type-is": "~1.6.18", 652 | "utils-merge": "1.0.1", 653 | "vary": "~1.1.2" 654 | }, 655 | "dependencies": { 656 | "debug": { 657 | "version": "2.6.9", 658 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 659 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 660 | "requires": { 661 | "ms": "2.0.0" 662 | } 663 | } 664 | } 665 | }, 666 | "extend": { 667 | "version": "3.0.2", 668 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 669 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 670 | }, 671 | "finalhandler": { 672 | "version": "1.1.2", 673 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 674 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 675 | "requires": { 676 | "debug": "2.6.9", 677 | "encodeurl": "~1.0.2", 678 | "escape-html": "~1.0.3", 679 | "on-finished": "~2.3.0", 680 | "parseurl": "~1.3.3", 681 | "statuses": "~1.5.0", 682 | "unpipe": "~1.0.0" 683 | }, 684 | "dependencies": { 685 | "debug": { 686 | "version": "2.6.9", 687 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 688 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 689 | "requires": { 690 | "ms": "2.0.0" 691 | } 692 | } 693 | } 694 | }, 695 | "find-cache-dir": { 696 | "version": "2.1.0", 697 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", 698 | "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", 699 | "requires": { 700 | "commondir": "^1.0.1", 701 | "make-dir": "^2.0.0", 702 | "pkg-dir": "^3.0.0" 703 | } 704 | }, 705 | "find-up": { 706 | "version": "3.0.0", 707 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 708 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 709 | "requires": { 710 | "locate-path": "^3.0.0" 711 | } 712 | }, 713 | "follow-redirects": { 714 | "version": "1.5.10", 715 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", 716 | "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", 717 | "requires": { 718 | "debug": "=3.1.0" 719 | } 720 | }, 721 | "foreground-child": { 722 | "version": "1.5.6", 723 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", 724 | "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", 725 | "requires": { 726 | "cross-spawn": "^4", 727 | "signal-exit": "^3.0.0" 728 | } 729 | }, 730 | "form-data": { 731 | "version": "2.5.1", 732 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", 733 | "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", 734 | "requires": { 735 | "asynckit": "^0.4.0", 736 | "combined-stream": "^1.0.6", 737 | "mime-types": "^2.1.12" 738 | } 739 | }, 740 | "formidable": { 741 | "version": "1.2.1", 742 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", 743 | "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" 744 | }, 745 | "forwarded": { 746 | "version": "0.1.2", 747 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 748 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 749 | }, 750 | "fresh": { 751 | "version": "0.5.2", 752 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 753 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 754 | }, 755 | "fs.realpath": { 756 | "version": "1.0.0", 757 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 758 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 759 | }, 760 | "function-bind": { 761 | "version": "1.1.1", 762 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 763 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 764 | }, 765 | "get-caller-file": { 766 | "version": "2.0.5", 767 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 768 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 769 | }, 770 | "glob": { 771 | "version": "7.1.6", 772 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 773 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 774 | "requires": { 775 | "fs.realpath": "^1.0.0", 776 | "inflight": "^1.0.4", 777 | "inherits": "2", 778 | "minimatch": "^3.0.4", 779 | "once": "^1.3.0", 780 | "path-is-absolute": "^1.0.0" 781 | } 782 | }, 783 | "globals": { 784 | "version": "11.12.0", 785 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 786 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" 787 | }, 788 | "graceful-fs": { 789 | "version": "4.2.3", 790 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 791 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" 792 | }, 793 | "handlebars": { 794 | "version": "4.5.3", 795 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", 796 | "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", 797 | "requires": { 798 | "neo-async": "^2.6.0", 799 | "optimist": "^0.6.1", 800 | "source-map": "^0.6.1", 801 | "uglify-js": "^3.1.4" 802 | }, 803 | "dependencies": { 804 | "source-map": { 805 | "version": "0.6.1", 806 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 807 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 808 | } 809 | } 810 | }, 811 | "has": { 812 | "version": "1.0.3", 813 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 814 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 815 | "requires": { 816 | "function-bind": "^1.1.1" 817 | } 818 | }, 819 | "has-flag": { 820 | "version": "3.0.0", 821 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 822 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 823 | }, 824 | "hasha": { 825 | "version": "3.0.0", 826 | "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", 827 | "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", 828 | "requires": { 829 | "is-stream": "^1.0.1" 830 | } 831 | }, 832 | "hoek": { 833 | "version": "6.1.3", 834 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", 835 | "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" 836 | }, 837 | "hosted-git-info": { 838 | "version": "2.8.5", 839 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", 840 | "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==" 841 | }, 842 | "http-errors": { 843 | "version": "1.7.2", 844 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 845 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 846 | "requires": { 847 | "depd": "~1.1.2", 848 | "inherits": "2.0.3", 849 | "setprototypeof": "1.1.1", 850 | "statuses": ">= 1.5.0 < 2", 851 | "toidentifier": "1.0.0" 852 | } 853 | }, 854 | "iconv-lite": { 855 | "version": "0.4.24", 856 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 857 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 858 | "requires": { 859 | "safer-buffer": ">= 2.1.2 < 3" 860 | } 861 | }, 862 | "imurmurhash": { 863 | "version": "0.1.4", 864 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 865 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 866 | }, 867 | "inflight": { 868 | "version": "1.0.6", 869 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 870 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 871 | "requires": { 872 | "once": "^1.3.0", 873 | "wrappy": "1" 874 | } 875 | }, 876 | "inherits": { 877 | "version": "2.0.3", 878 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 879 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 880 | }, 881 | "ipaddr.js": { 882 | "version": "1.9.0", 883 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 884 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 885 | }, 886 | "is-arrayish": { 887 | "version": "0.2.1", 888 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 889 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 890 | }, 891 | "is-buffer": { 892 | "version": "2.0.4", 893 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 894 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" 895 | }, 896 | "is-expression": { 897 | "version": "3.0.0", 898 | "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", 899 | "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", 900 | "requires": { 901 | "acorn": "~4.0.2", 902 | "object-assign": "^4.0.1" 903 | }, 904 | "dependencies": { 905 | "acorn": { 906 | "version": "4.0.13", 907 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", 908 | "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" 909 | } 910 | } 911 | }, 912 | "is-fullwidth-code-point": { 913 | "version": "2.0.0", 914 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 915 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 916 | }, 917 | "is-promise": { 918 | "version": "2.1.0", 919 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 920 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" 921 | }, 922 | "is-regex": { 923 | "version": "1.0.4", 924 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 925 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 926 | "requires": { 927 | "has": "^1.0.1" 928 | } 929 | }, 930 | "is-stream": { 931 | "version": "1.1.0", 932 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 933 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 934 | }, 935 | "isarray": { 936 | "version": "1.0.0", 937 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 938 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 939 | }, 940 | "isexe": { 941 | "version": "2.0.0", 942 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 943 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 944 | }, 945 | "istanbul-lib-coverage": { 946 | "version": "2.0.5", 947 | "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", 948 | "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" 949 | }, 950 | "istanbul-lib-hook": { 951 | "version": "2.0.7", 952 | "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", 953 | "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", 954 | "requires": { 955 | "append-transform": "^1.0.0" 956 | } 957 | }, 958 | "istanbul-lib-instrument": { 959 | "version": "3.3.0", 960 | "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", 961 | "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", 962 | "requires": { 963 | "@babel/generator": "^7.4.0", 964 | "@babel/parser": "^7.4.3", 965 | "@babel/template": "^7.4.0", 966 | "@babel/traverse": "^7.4.3", 967 | "@babel/types": "^7.4.0", 968 | "istanbul-lib-coverage": "^2.0.5", 969 | "semver": "^6.0.0" 970 | }, 971 | "dependencies": { 972 | "semver": { 973 | "version": "6.3.0", 974 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 975 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 976 | } 977 | } 978 | }, 979 | "istanbul-lib-report": { 980 | "version": "2.0.8", 981 | "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", 982 | "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", 983 | "requires": { 984 | "istanbul-lib-coverage": "^2.0.5", 985 | "make-dir": "^2.1.0", 986 | "supports-color": "^6.1.0" 987 | }, 988 | "dependencies": { 989 | "supports-color": { 990 | "version": "6.1.0", 991 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", 992 | "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", 993 | "requires": { 994 | "has-flag": "^3.0.0" 995 | } 996 | } 997 | } 998 | }, 999 | "istanbul-lib-source-maps": { 1000 | "version": "3.0.6", 1001 | "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", 1002 | "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", 1003 | "requires": { 1004 | "debug": "^4.1.1", 1005 | "istanbul-lib-coverage": "^2.0.5", 1006 | "make-dir": "^2.1.0", 1007 | "rimraf": "^2.6.3", 1008 | "source-map": "^0.6.1" 1009 | }, 1010 | "dependencies": { 1011 | "debug": { 1012 | "version": "4.1.1", 1013 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1014 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1015 | "requires": { 1016 | "ms": "^2.1.1" 1017 | } 1018 | }, 1019 | "ms": { 1020 | "version": "2.1.2", 1021 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1022 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1023 | }, 1024 | "source-map": { 1025 | "version": "0.6.1", 1026 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1027 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 1028 | } 1029 | } 1030 | }, 1031 | "istanbul-reports": { 1032 | "version": "2.2.6", 1033 | "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", 1034 | "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", 1035 | "requires": { 1036 | "handlebars": "^4.1.2" 1037 | } 1038 | }, 1039 | "js-stringify": { 1040 | "version": "1.0.2", 1041 | "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", 1042 | "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" 1043 | }, 1044 | "js-tokens": { 1045 | "version": "4.0.0", 1046 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1047 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 1048 | }, 1049 | "js-yaml": { 1050 | "version": "3.13.1", 1051 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 1052 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 1053 | "requires": { 1054 | "argparse": "^1.0.7", 1055 | "esprima": "^4.0.0" 1056 | } 1057 | }, 1058 | "jsesc": { 1059 | "version": "2.5.2", 1060 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 1061 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" 1062 | }, 1063 | "json-parse-better-errors": { 1064 | "version": "1.0.2", 1065 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 1066 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" 1067 | }, 1068 | "jsonwebtoken": { 1069 | "version": "5.7.0", 1070 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz", 1071 | "integrity": "sha1-HJD5qGzlt0j1+XnBK3BAK0r83bQ=", 1072 | "requires": { 1073 | "jws": "^3.0.0", 1074 | "ms": "^0.7.1", 1075 | "xtend": "^4.0.1" 1076 | }, 1077 | "dependencies": { 1078 | "ms": { 1079 | "version": "0.7.3", 1080 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", 1081 | "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=" 1082 | } 1083 | } 1084 | }, 1085 | "jstransformer": { 1086 | "version": "1.0.0", 1087 | "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", 1088 | "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", 1089 | "requires": { 1090 | "is-promise": "^2.0.0", 1091 | "promise": "^7.0.1" 1092 | } 1093 | }, 1094 | "jwa": { 1095 | "version": "1.4.1", 1096 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 1097 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 1098 | "requires": { 1099 | "buffer-equal-constant-time": "1.0.1", 1100 | "ecdsa-sig-formatter": "1.0.11", 1101 | "safe-buffer": "^5.0.1" 1102 | } 1103 | }, 1104 | "jws": { 1105 | "version": "3.2.2", 1106 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 1107 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 1108 | "requires": { 1109 | "jwa": "^1.4.1", 1110 | "safe-buffer": "^5.0.1" 1111 | } 1112 | }, 1113 | "kind-of": { 1114 | "version": "3.2.2", 1115 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1116 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1117 | "requires": { 1118 | "is-buffer": "^1.1.5" 1119 | }, 1120 | "dependencies": { 1121 | "is-buffer": { 1122 | "version": "1.1.6", 1123 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 1124 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 1125 | } 1126 | } 1127 | }, 1128 | "lazy-cache": { 1129 | "version": "1.0.4", 1130 | "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", 1131 | "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" 1132 | }, 1133 | "load-json-file": { 1134 | "version": "4.0.0", 1135 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 1136 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 1137 | "requires": { 1138 | "graceful-fs": "^4.1.2", 1139 | "parse-json": "^4.0.0", 1140 | "pify": "^3.0.0", 1141 | "strip-bom": "^3.0.0" 1142 | }, 1143 | "dependencies": { 1144 | "pify": { 1145 | "version": "3.0.0", 1146 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1147 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1148 | } 1149 | } 1150 | }, 1151 | "locate-path": { 1152 | "version": "3.0.0", 1153 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 1154 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 1155 | "requires": { 1156 | "p-locate": "^3.0.0", 1157 | "path-exists": "^3.0.0" 1158 | } 1159 | }, 1160 | "lodash": { 1161 | "version": "4.17.15", 1162 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1163 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 1164 | }, 1165 | "lodash.flattendeep": { 1166 | "version": "4.4.0", 1167 | "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", 1168 | "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=" 1169 | }, 1170 | "longest": { 1171 | "version": "1.0.1", 1172 | "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", 1173 | "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" 1174 | }, 1175 | "lru-cache": { 1176 | "version": "4.1.5", 1177 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 1178 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 1179 | "requires": { 1180 | "pseudomap": "^1.0.2", 1181 | "yallist": "^2.1.2" 1182 | } 1183 | }, 1184 | "make-dir": { 1185 | "version": "2.1.0", 1186 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", 1187 | "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", 1188 | "requires": { 1189 | "pify": "^4.0.1", 1190 | "semver": "^5.6.0" 1191 | } 1192 | }, 1193 | "media-typer": { 1194 | "version": "0.3.0", 1195 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1196 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1197 | }, 1198 | "merge-descriptors": { 1199 | "version": "1.0.1", 1200 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1201 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1202 | }, 1203 | "merge-source-map": { 1204 | "version": "1.1.0", 1205 | "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", 1206 | "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", 1207 | "requires": { 1208 | "source-map": "^0.6.1" 1209 | }, 1210 | "dependencies": { 1211 | "source-map": { 1212 | "version": "0.6.1", 1213 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1214 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 1215 | } 1216 | } 1217 | }, 1218 | "methods": { 1219 | "version": "1.1.2", 1220 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1221 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1222 | }, 1223 | "mime": { 1224 | "version": "1.6.0", 1225 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1226 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1227 | }, 1228 | "mime-db": { 1229 | "version": "1.42.0", 1230 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", 1231 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" 1232 | }, 1233 | "mime-types": { 1234 | "version": "2.1.25", 1235 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", 1236 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", 1237 | "requires": { 1238 | "mime-db": "1.42.0" 1239 | } 1240 | }, 1241 | "minimatch": { 1242 | "version": "3.0.4", 1243 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1244 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1245 | "requires": { 1246 | "brace-expansion": "^1.1.7" 1247 | } 1248 | }, 1249 | "minimist": { 1250 | "version": "0.0.8", 1251 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1252 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1253 | }, 1254 | "mkdirp": { 1255 | "version": "0.5.1", 1256 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1257 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1258 | "requires": { 1259 | "minimist": "0.0.8" 1260 | } 1261 | }, 1262 | "ms": { 1263 | "version": "2.0.0", 1264 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1265 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1266 | }, 1267 | "negotiator": { 1268 | "version": "0.6.2", 1269 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 1270 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 1271 | }, 1272 | "neo-async": { 1273 | "version": "2.6.1", 1274 | "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", 1275 | "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" 1276 | }, 1277 | "nested-error-stacks": { 1278 | "version": "2.1.0", 1279 | "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", 1280 | "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" 1281 | }, 1282 | "normalize-package-data": { 1283 | "version": "2.5.0", 1284 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", 1285 | "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", 1286 | "requires": { 1287 | "hosted-git-info": "^2.1.4", 1288 | "resolve": "^1.10.0", 1289 | "semver": "2 || 3 || 4 || 5", 1290 | "validate-npm-package-license": "^3.0.1" 1291 | } 1292 | }, 1293 | "nyc": { 1294 | "version": "14.1.1", 1295 | "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", 1296 | "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", 1297 | "requires": { 1298 | "archy": "^1.0.0", 1299 | "caching-transform": "^3.0.2", 1300 | "convert-source-map": "^1.6.0", 1301 | "cp-file": "^6.2.0", 1302 | "find-cache-dir": "^2.1.0", 1303 | "find-up": "^3.0.0", 1304 | "foreground-child": "^1.5.6", 1305 | "glob": "^7.1.3", 1306 | "istanbul-lib-coverage": "^2.0.5", 1307 | "istanbul-lib-hook": "^2.0.7", 1308 | "istanbul-lib-instrument": "^3.3.0", 1309 | "istanbul-lib-report": "^2.0.8", 1310 | "istanbul-lib-source-maps": "^3.0.6", 1311 | "istanbul-reports": "^2.2.4", 1312 | "js-yaml": "^3.13.1", 1313 | "make-dir": "^2.1.0", 1314 | "merge-source-map": "^1.1.0", 1315 | "resolve-from": "^4.0.0", 1316 | "rimraf": "^2.6.3", 1317 | "signal-exit": "^3.0.2", 1318 | "spawn-wrap": "^1.4.2", 1319 | "test-exclude": "^5.2.3", 1320 | "uuid": "^3.3.2", 1321 | "yargs": "^13.2.2", 1322 | "yargs-parser": "^13.0.0" 1323 | } 1324 | }, 1325 | "object-assign": { 1326 | "version": "4.1.1", 1327 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1328 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1329 | }, 1330 | "on-finished": { 1331 | "version": "2.3.0", 1332 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1333 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1334 | "requires": { 1335 | "ee-first": "1.1.1" 1336 | } 1337 | }, 1338 | "once": { 1339 | "version": "1.4.0", 1340 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1341 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1342 | "requires": { 1343 | "wrappy": "1" 1344 | } 1345 | }, 1346 | "optimist": { 1347 | "version": "0.6.1", 1348 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1349 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 1350 | "requires": { 1351 | "minimist": "~0.0.1", 1352 | "wordwrap": "~0.0.2" 1353 | } 1354 | }, 1355 | "os-homedir": { 1356 | "version": "1.0.2", 1357 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1358 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 1359 | }, 1360 | "p-limit": { 1361 | "version": "2.2.1", 1362 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", 1363 | "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", 1364 | "requires": { 1365 | "p-try": "^2.0.0" 1366 | } 1367 | }, 1368 | "p-locate": { 1369 | "version": "3.0.0", 1370 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1371 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1372 | "requires": { 1373 | "p-limit": "^2.0.0" 1374 | } 1375 | }, 1376 | "p-try": { 1377 | "version": "2.2.0", 1378 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1379 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 1380 | }, 1381 | "package-hash": { 1382 | "version": "3.0.0", 1383 | "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", 1384 | "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", 1385 | "requires": { 1386 | "graceful-fs": "^4.1.15", 1387 | "hasha": "^3.0.0", 1388 | "lodash.flattendeep": "^4.4.0", 1389 | "release-zalgo": "^1.0.0" 1390 | } 1391 | }, 1392 | "parse-json": { 1393 | "version": "4.0.0", 1394 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 1395 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 1396 | "requires": { 1397 | "error-ex": "^1.3.1", 1398 | "json-parse-better-errors": "^1.0.1" 1399 | } 1400 | }, 1401 | "parseurl": { 1402 | "version": "1.3.3", 1403 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1404 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1405 | }, 1406 | "path-exists": { 1407 | "version": "3.0.0", 1408 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1409 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 1410 | }, 1411 | "path-is-absolute": { 1412 | "version": "1.0.1", 1413 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1414 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1415 | }, 1416 | "path-parse": { 1417 | "version": "1.0.6", 1418 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1419 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" 1420 | }, 1421 | "path-to-regexp": { 1422 | "version": "0.1.7", 1423 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1424 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1425 | }, 1426 | "path-type": { 1427 | "version": "3.0.0", 1428 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 1429 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 1430 | "requires": { 1431 | "pify": "^3.0.0" 1432 | }, 1433 | "dependencies": { 1434 | "pify": { 1435 | "version": "3.0.0", 1436 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1437 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1438 | } 1439 | } 1440 | }, 1441 | "pify": { 1442 | "version": "4.0.1", 1443 | "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", 1444 | "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" 1445 | }, 1446 | "pkg-dir": { 1447 | "version": "3.0.0", 1448 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", 1449 | "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", 1450 | "requires": { 1451 | "find-up": "^3.0.0" 1452 | } 1453 | }, 1454 | "process-nextick-args": { 1455 | "version": "2.0.1", 1456 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1457 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1458 | }, 1459 | "promise": { 1460 | "version": "7.3.1", 1461 | "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", 1462 | "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", 1463 | "requires": { 1464 | "asap": "~2.0.3" 1465 | } 1466 | }, 1467 | "proxy-addr": { 1468 | "version": "2.0.5", 1469 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 1470 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 1471 | "requires": { 1472 | "forwarded": "~0.1.2", 1473 | "ipaddr.js": "1.9.0" 1474 | } 1475 | }, 1476 | "pseudomap": { 1477 | "version": "1.0.2", 1478 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1479 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 1480 | }, 1481 | "pug": { 1482 | "version": "2.0.4", 1483 | "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", 1484 | "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", 1485 | "requires": { 1486 | "pug-code-gen": "^2.0.2", 1487 | "pug-filters": "^3.1.1", 1488 | "pug-lexer": "^4.1.0", 1489 | "pug-linker": "^3.0.6", 1490 | "pug-load": "^2.0.12", 1491 | "pug-parser": "^5.0.1", 1492 | "pug-runtime": "^2.0.5", 1493 | "pug-strip-comments": "^1.0.4" 1494 | } 1495 | }, 1496 | "pug-attrs": { 1497 | "version": "2.0.4", 1498 | "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", 1499 | "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", 1500 | "requires": { 1501 | "constantinople": "^3.0.1", 1502 | "js-stringify": "^1.0.1", 1503 | "pug-runtime": "^2.0.5" 1504 | } 1505 | }, 1506 | "pug-code-gen": { 1507 | "version": "2.0.2", 1508 | "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", 1509 | "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", 1510 | "requires": { 1511 | "constantinople": "^3.1.2", 1512 | "doctypes": "^1.1.0", 1513 | "js-stringify": "^1.0.1", 1514 | "pug-attrs": "^2.0.4", 1515 | "pug-error": "^1.3.3", 1516 | "pug-runtime": "^2.0.5", 1517 | "void-elements": "^2.0.1", 1518 | "with": "^5.0.0" 1519 | } 1520 | }, 1521 | "pug-error": { 1522 | "version": "1.3.3", 1523 | "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", 1524 | "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==" 1525 | }, 1526 | "pug-filters": { 1527 | "version": "3.1.1", 1528 | "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", 1529 | "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", 1530 | "requires": { 1531 | "clean-css": "^4.1.11", 1532 | "constantinople": "^3.0.1", 1533 | "jstransformer": "1.0.0", 1534 | "pug-error": "^1.3.3", 1535 | "pug-walk": "^1.1.8", 1536 | "resolve": "^1.1.6", 1537 | "uglify-js": "^2.6.1" 1538 | }, 1539 | "dependencies": { 1540 | "camelcase": { 1541 | "version": "1.2.1", 1542 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 1543 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" 1544 | }, 1545 | "cliui": { 1546 | "version": "2.1.0", 1547 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", 1548 | "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", 1549 | "requires": { 1550 | "center-align": "^0.1.1", 1551 | "right-align": "^0.1.1", 1552 | "wordwrap": "0.0.2" 1553 | } 1554 | }, 1555 | "uglify-js": { 1556 | "version": "2.8.29", 1557 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", 1558 | "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", 1559 | "requires": { 1560 | "source-map": "~0.5.1", 1561 | "uglify-to-browserify": "~1.0.0", 1562 | "yargs": "~3.10.0" 1563 | } 1564 | }, 1565 | "wordwrap": { 1566 | "version": "0.0.2", 1567 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 1568 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" 1569 | }, 1570 | "yargs": { 1571 | "version": "3.10.0", 1572 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", 1573 | "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", 1574 | "requires": { 1575 | "camelcase": "^1.0.2", 1576 | "cliui": "^2.1.0", 1577 | "decamelize": "^1.0.0", 1578 | "window-size": "0.1.0" 1579 | } 1580 | } 1581 | } 1582 | }, 1583 | "pug-lexer": { 1584 | "version": "4.1.0", 1585 | "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", 1586 | "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", 1587 | "requires": { 1588 | "character-parser": "^2.1.1", 1589 | "is-expression": "^3.0.0", 1590 | "pug-error": "^1.3.3" 1591 | } 1592 | }, 1593 | "pug-linker": { 1594 | "version": "3.0.6", 1595 | "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", 1596 | "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", 1597 | "requires": { 1598 | "pug-error": "^1.3.3", 1599 | "pug-walk": "^1.1.8" 1600 | } 1601 | }, 1602 | "pug-load": { 1603 | "version": "2.0.12", 1604 | "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", 1605 | "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", 1606 | "requires": { 1607 | "object-assign": "^4.1.0", 1608 | "pug-walk": "^1.1.8" 1609 | } 1610 | }, 1611 | "pug-parser": { 1612 | "version": "5.0.1", 1613 | "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", 1614 | "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", 1615 | "requires": { 1616 | "pug-error": "^1.3.3", 1617 | "token-stream": "0.0.1" 1618 | } 1619 | }, 1620 | "pug-runtime": { 1621 | "version": "2.0.5", 1622 | "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", 1623 | "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==" 1624 | }, 1625 | "pug-strip-comments": { 1626 | "version": "1.0.4", 1627 | "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", 1628 | "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", 1629 | "requires": { 1630 | "pug-error": "^1.3.3" 1631 | } 1632 | }, 1633 | "pug-walk": { 1634 | "version": "1.1.8", 1635 | "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", 1636 | "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==" 1637 | }, 1638 | "qs": { 1639 | "version": "6.7.0", 1640 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1641 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 1642 | }, 1643 | "range-parser": { 1644 | "version": "1.2.1", 1645 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1646 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1647 | }, 1648 | "raw-body": { 1649 | "version": "2.4.0", 1650 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1651 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1652 | "requires": { 1653 | "bytes": "3.1.0", 1654 | "http-errors": "1.7.2", 1655 | "iconv-lite": "0.4.24", 1656 | "unpipe": "1.0.0" 1657 | } 1658 | }, 1659 | "read-pkg": { 1660 | "version": "3.0.0", 1661 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 1662 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 1663 | "requires": { 1664 | "load-json-file": "^4.0.0", 1665 | "normalize-package-data": "^2.3.2", 1666 | "path-type": "^3.0.0" 1667 | } 1668 | }, 1669 | "read-pkg-up": { 1670 | "version": "4.0.0", 1671 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", 1672 | "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", 1673 | "requires": { 1674 | "find-up": "^3.0.0", 1675 | "read-pkg": "^3.0.0" 1676 | } 1677 | }, 1678 | "readable-stream": { 1679 | "version": "2.3.6", 1680 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1681 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1682 | "requires": { 1683 | "core-util-is": "~1.0.0", 1684 | "inherits": "~2.0.3", 1685 | "isarray": "~1.0.0", 1686 | "process-nextick-args": "~2.0.0", 1687 | "safe-buffer": "~5.1.1", 1688 | "string_decoder": "~1.1.1", 1689 | "util-deprecate": "~1.0.1" 1690 | } 1691 | }, 1692 | "regenerator-runtime": { 1693 | "version": "0.11.1", 1694 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", 1695 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" 1696 | }, 1697 | "release-zalgo": { 1698 | "version": "1.0.0", 1699 | "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", 1700 | "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", 1701 | "requires": { 1702 | "es6-error": "^4.0.1" 1703 | } 1704 | }, 1705 | "repeat-string": { 1706 | "version": "1.6.1", 1707 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 1708 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" 1709 | }, 1710 | "require-directory": { 1711 | "version": "2.1.1", 1712 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1713 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1714 | }, 1715 | "require-main-filename": { 1716 | "version": "2.0.0", 1717 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1718 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 1719 | }, 1720 | "resolve": { 1721 | "version": "1.12.0", 1722 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", 1723 | "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", 1724 | "requires": { 1725 | "path-parse": "^1.0.6" 1726 | } 1727 | }, 1728 | "resolve-from": { 1729 | "version": "4.0.0", 1730 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1731 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 1732 | }, 1733 | "right-align": { 1734 | "version": "0.1.3", 1735 | "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", 1736 | "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", 1737 | "requires": { 1738 | "align-text": "^0.1.1" 1739 | } 1740 | }, 1741 | "rimraf": { 1742 | "version": "2.7.1", 1743 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 1744 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 1745 | "requires": { 1746 | "glob": "^7.1.3" 1747 | } 1748 | }, 1749 | "safe-buffer": { 1750 | "version": "5.1.2", 1751 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1752 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1753 | }, 1754 | "safer-buffer": { 1755 | "version": "2.1.2", 1756 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1757 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1758 | }, 1759 | "semver": { 1760 | "version": "5.7.1", 1761 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1762 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1763 | }, 1764 | "send": { 1765 | "version": "0.17.1", 1766 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1767 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1768 | "requires": { 1769 | "debug": "2.6.9", 1770 | "depd": "~1.1.2", 1771 | "destroy": "~1.0.4", 1772 | "encodeurl": "~1.0.2", 1773 | "escape-html": "~1.0.3", 1774 | "etag": "~1.8.1", 1775 | "fresh": "0.5.2", 1776 | "http-errors": "~1.7.2", 1777 | "mime": "1.6.0", 1778 | "ms": "2.1.1", 1779 | "on-finished": "~2.3.0", 1780 | "range-parser": "~1.2.1", 1781 | "statuses": "~1.5.0" 1782 | }, 1783 | "dependencies": { 1784 | "debug": { 1785 | "version": "2.6.9", 1786 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1787 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1788 | "requires": { 1789 | "ms": "2.0.0" 1790 | }, 1791 | "dependencies": { 1792 | "ms": { 1793 | "version": "2.0.0", 1794 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1795 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1796 | } 1797 | } 1798 | }, 1799 | "ms": { 1800 | "version": "2.1.1", 1801 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1802 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1803 | } 1804 | } 1805 | }, 1806 | "serve-static": { 1807 | "version": "1.14.1", 1808 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1809 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1810 | "requires": { 1811 | "encodeurl": "~1.0.2", 1812 | "escape-html": "~1.0.3", 1813 | "parseurl": "~1.3.3", 1814 | "send": "0.17.1" 1815 | } 1816 | }, 1817 | "set-blocking": { 1818 | "version": "2.0.0", 1819 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1820 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1821 | }, 1822 | "setprototypeof": { 1823 | "version": "1.1.1", 1824 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1825 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1826 | }, 1827 | "signal-exit": { 1828 | "version": "3.0.2", 1829 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1830 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1831 | }, 1832 | "source-map": { 1833 | "version": "0.5.7", 1834 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1835 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 1836 | }, 1837 | "spawn-wrap": { 1838 | "version": "1.4.3", 1839 | "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", 1840 | "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", 1841 | "requires": { 1842 | "foreground-child": "^1.5.6", 1843 | "mkdirp": "^0.5.0", 1844 | "os-homedir": "^1.0.1", 1845 | "rimraf": "^2.6.2", 1846 | "signal-exit": "^3.0.2", 1847 | "which": "^1.3.0" 1848 | } 1849 | }, 1850 | "spdx-correct": { 1851 | "version": "3.1.0", 1852 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", 1853 | "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", 1854 | "requires": { 1855 | "spdx-expression-parse": "^3.0.0", 1856 | "spdx-license-ids": "^3.0.0" 1857 | } 1858 | }, 1859 | "spdx-exceptions": { 1860 | "version": "2.2.0", 1861 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1862 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 1863 | }, 1864 | "spdx-expression-parse": { 1865 | "version": "3.0.0", 1866 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1867 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1868 | "requires": { 1869 | "spdx-exceptions": "^2.1.0", 1870 | "spdx-license-ids": "^3.0.0" 1871 | } 1872 | }, 1873 | "spdx-license-ids": { 1874 | "version": "3.0.5", 1875 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 1876 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" 1877 | }, 1878 | "sprintf-js": { 1879 | "version": "1.0.3", 1880 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1881 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1882 | }, 1883 | "statuses": { 1884 | "version": "1.5.0", 1885 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1886 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 1887 | }, 1888 | "string-width": { 1889 | "version": "3.1.0", 1890 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1891 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1892 | "requires": { 1893 | "emoji-regex": "^7.0.1", 1894 | "is-fullwidth-code-point": "^2.0.0", 1895 | "strip-ansi": "^5.1.0" 1896 | } 1897 | }, 1898 | "string_decoder": { 1899 | "version": "1.1.1", 1900 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1901 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1902 | "requires": { 1903 | "safe-buffer": "~5.1.0" 1904 | } 1905 | }, 1906 | "strip-ansi": { 1907 | "version": "5.2.0", 1908 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1909 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1910 | "requires": { 1911 | "ansi-regex": "^4.1.0" 1912 | } 1913 | }, 1914 | "strip-bom": { 1915 | "version": "3.0.0", 1916 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1917 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" 1918 | }, 1919 | "superagent": { 1920 | "version": "3.8.3", 1921 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", 1922 | "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", 1923 | "requires": { 1924 | "component-emitter": "^1.2.0", 1925 | "cookiejar": "^2.1.0", 1926 | "debug": "^3.1.0", 1927 | "extend": "^3.0.0", 1928 | "form-data": "^2.3.1", 1929 | "formidable": "^1.2.0", 1930 | "methods": "^1.1.1", 1931 | "mime": "^1.4.1", 1932 | "qs": "^6.5.1", 1933 | "readable-stream": "^2.3.5" 1934 | } 1935 | }, 1936 | "supports-color": { 1937 | "version": "5.5.0", 1938 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1939 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1940 | "requires": { 1941 | "has-flag": "^3.0.0" 1942 | } 1943 | }, 1944 | "test-exclude": { 1945 | "version": "5.2.3", 1946 | "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", 1947 | "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", 1948 | "requires": { 1949 | "glob": "^7.1.3", 1950 | "minimatch": "^3.0.4", 1951 | "read-pkg-up": "^4.0.0", 1952 | "require-main-filename": "^2.0.0" 1953 | } 1954 | }, 1955 | "tinytim": { 1956 | "version": "0.1.1", 1957 | "resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz", 1958 | "integrity": "sha1-yWih5VWa2VUyJO92J7qzTjyu+Kg=" 1959 | }, 1960 | "to-fast-properties": { 1961 | "version": "2.0.0", 1962 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 1963 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" 1964 | }, 1965 | "toidentifier": { 1966 | "version": "1.0.0", 1967 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 1968 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 1969 | }, 1970 | "token-stream": { 1971 | "version": "0.0.1", 1972 | "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", 1973 | "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" 1974 | }, 1975 | "tracer": { 1976 | "version": "1.0.1", 1977 | "resolved": "https://registry.npmjs.org/tracer/-/tracer-1.0.1.tgz", 1978 | "integrity": "sha512-/kxtMu2KWtdjQSzFzLnA8bYpDKHlcK2BGX4e6r7n0dbD7BGTQjGmwZKNHwFPQaHjzBHtNtWva0frymfYtAWZyw==", 1979 | "requires": { 1980 | "colors": "1.3.3", 1981 | "dateformat": "3.0.3", 1982 | "mkdirp": "^0.5.1", 1983 | "nyc": "^14.1.1", 1984 | "tinytim": "0.1.1" 1985 | } 1986 | }, 1987 | "type-is": { 1988 | "version": "1.6.18", 1989 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1990 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1991 | "requires": { 1992 | "media-typer": "0.3.0", 1993 | "mime-types": "~2.1.24" 1994 | } 1995 | }, 1996 | "uglify-js": { 1997 | "version": "3.6.9", 1998 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", 1999 | "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==", 2000 | "optional": true, 2001 | "requires": { 2002 | "commander": "~2.20.3", 2003 | "source-map": "~0.6.1" 2004 | }, 2005 | "dependencies": { 2006 | "source-map": { 2007 | "version": "0.6.1", 2008 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2009 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2010 | "optional": true 2011 | } 2012 | } 2013 | }, 2014 | "uglify-to-browserify": { 2015 | "version": "1.0.2", 2016 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 2017 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 2018 | "optional": true 2019 | }, 2020 | "unpipe": { 2021 | "version": "1.0.0", 2022 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2023 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2024 | }, 2025 | "util-deprecate": { 2026 | "version": "1.0.2", 2027 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2028 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 2029 | }, 2030 | "utils-merge": { 2031 | "version": "1.0.1", 2032 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2033 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 2034 | }, 2035 | "uuid": { 2036 | "version": "3.3.3", 2037 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", 2038 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" 2039 | }, 2040 | "validate-npm-package-license": { 2041 | "version": "3.0.4", 2042 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 2043 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 2044 | "requires": { 2045 | "spdx-correct": "^3.0.0", 2046 | "spdx-expression-parse": "^3.0.0" 2047 | } 2048 | }, 2049 | "vary": { 2050 | "version": "1.1.2", 2051 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2052 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 2053 | }, 2054 | "void-elements": { 2055 | "version": "2.0.1", 2056 | "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", 2057 | "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" 2058 | }, 2059 | "webtask-tools": { 2060 | "version": "3.4.1", 2061 | "resolved": "https://registry.npmjs.org/webtask-tools/-/webtask-tools-3.4.1.tgz", 2062 | "integrity": "sha512-NqjvLZVA3AUsRLWBIEcNH7jEoBZERjb1w3JANg/KjxUkEcmS+c8bcBPAGIy+awhgVax2E52qs6bZhiPg842aCw==", 2063 | "requires": { 2064 | "boom": "^7.2.0", 2065 | "jsonwebtoken": "^5.7.0", 2066 | "pug": "^2.0.3", 2067 | "safe-buffer": "^5.0.1", 2068 | "superagent": "^3.8.3" 2069 | } 2070 | }, 2071 | "which": { 2072 | "version": "1.3.1", 2073 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2074 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2075 | "requires": { 2076 | "isexe": "^2.0.0" 2077 | } 2078 | }, 2079 | "which-module": { 2080 | "version": "2.0.0", 2081 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 2082 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 2083 | }, 2084 | "window-size": { 2085 | "version": "0.1.0", 2086 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 2087 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" 2088 | }, 2089 | "with": { 2090 | "version": "5.1.1", 2091 | "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", 2092 | "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", 2093 | "requires": { 2094 | "acorn": "^3.1.0", 2095 | "acorn-globals": "^3.0.0" 2096 | } 2097 | }, 2098 | "wordwrap": { 2099 | "version": "0.0.3", 2100 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 2101 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" 2102 | }, 2103 | "wrap-ansi": { 2104 | "version": "5.1.0", 2105 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 2106 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 2107 | "requires": { 2108 | "ansi-styles": "^3.2.0", 2109 | "string-width": "^3.0.0", 2110 | "strip-ansi": "^5.0.0" 2111 | } 2112 | }, 2113 | "wrappy": { 2114 | "version": "1.0.2", 2115 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2116 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 2117 | }, 2118 | "write-file-atomic": { 2119 | "version": "2.4.3", 2120 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", 2121 | "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", 2122 | "requires": { 2123 | "graceful-fs": "^4.1.11", 2124 | "imurmurhash": "^0.1.4", 2125 | "signal-exit": "^3.0.2" 2126 | } 2127 | }, 2128 | "xtend": { 2129 | "version": "4.0.2", 2130 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 2131 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 2132 | }, 2133 | "y18n": { 2134 | "version": "4.0.0", 2135 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 2136 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 2137 | }, 2138 | "yallist": { 2139 | "version": "2.1.2", 2140 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 2141 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 2142 | }, 2143 | "yargs": { 2144 | "version": "13.3.0", 2145 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", 2146 | "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", 2147 | "requires": { 2148 | "cliui": "^5.0.0", 2149 | "find-up": "^3.0.0", 2150 | "get-caller-file": "^2.0.1", 2151 | "require-directory": "^2.1.1", 2152 | "require-main-filename": "^2.0.0", 2153 | "set-blocking": "^2.0.0", 2154 | "string-width": "^3.0.0", 2155 | "which-module": "^2.0.0", 2156 | "y18n": "^4.0.0", 2157 | "yargs-parser": "^13.1.1" 2158 | } 2159 | }, 2160 | "yargs-parser": { 2161 | "version": "13.1.1", 2162 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", 2163 | "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", 2164 | "requires": { 2165 | "camelcase": "^5.0.0", 2166 | "decamelize": "^1.2.0" 2167 | } 2168 | } 2169 | } 2170 | } 2171 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pocket-proxy-server", 3 | "version": "0.1.0", 4 | "engines": { 5 | "node": "8" 6 | }, 7 | "scripts": { 8 | "start": "wt serve pocket-proxy-server.js", 9 | "create": "wt create pocket-proxy-server.js -b --secrets-file .authproxy.env", 10 | "update": "wt update pocket-proxy-server pocket-proxy-server.js -b", 11 | "dev": "wt create pocket-proxy-server.js -b --secrets-file .authproxy.env --name pocket-proxy-server-dev", 12 | "update:dev": "wt update pocket-proxy-server-dev pocket-proxy-server.js -b -w" 13 | }, 14 | "dependencies": { 15 | "axios": "0.19.0", 16 | "express": "4.17.1", 17 | "tracer": "1.0.1", 18 | "webtask-tools": "3.4.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/pocket-cli-auth-proxy/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless -------------------------------------------------------------------------------- /server/pocket-cli-auth-proxy/handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports.hello = async event => { 4 | return { 5 | statusCode: 200, 6 | body: JSON.stringify( 7 | { 8 | message: 'Go Serverless v1.0! Your function executed successfully!', 9 | input: event, 10 | }, 11 | null, 12 | 2 13 | ), 14 | }; 15 | 16 | // Use this code if you don't use the http event with the LAMBDA-PROXY integration 17 | // return { message: 'Go Serverless v1.0! Your function executed successfully!', event }; 18 | }; 19 | -------------------------------------------------------------------------------- /server/pocket-cli-auth-proxy/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: pocket-cli-auth-proxy 15 | # app and org for use with dashboard.serverless.com 16 | #app: your-app-name 17 | #org: your-org-name 18 | 19 | # You can pin your service to only deploy with a specific Serverless version 20 | # Check out our docs for more details 21 | # frameworkVersion: "=X.X.X" 22 | 23 | provider: 24 | name: aws 25 | runtime: nodejs12.x 26 | 27 | # you can overwrite defaults here 28 | # stage: dev 29 | # region: us-east-1 30 | 31 | # you can add statements to the Lambda function's IAM Role here 32 | # iamRoleStatements: 33 | # - Effect: "Allow" 34 | # Action: 35 | # - "s3:ListBucket" 36 | # Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } 37 | # - Effect: "Allow" 38 | # Action: 39 | # - "s3:PutObject" 40 | # Resource: 41 | # Fn::Join: 42 | # - "" 43 | # - - "arn:aws:s3:::" 44 | # - "Ref" : "ServerlessDeploymentBucket" 45 | # - "/*" 46 | 47 | # you can define service wide environment variables here 48 | # environment: 49 | # variable1: value1 50 | 51 | # you can add packaging information here 52 | #package: 53 | # include: 54 | # - include-me.js 55 | # - include-me-dir/** 56 | # exclude: 57 | # - exclude-me.js 58 | # - exclude-me-dir/** 59 | 60 | functions: 61 | hello: 62 | handler: handler.hello 63 | # The following are a few example events you can configure 64 | # NOTE: Please make sure to change your handler code to work with those events 65 | # Check the event documentation for details 66 | # events: 67 | # - http: 68 | # path: users/create 69 | # method: get 70 | # - websocket: $connect 71 | # - s3: ${env:BUCKET} 72 | # - schedule: rate(10 minutes) 73 | # - sns: greeter-topic 74 | # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 75 | # - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx 76 | # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx 77 | # - iot: 78 | # sql: "SELECT * FROM 'some_topic'" 79 | # - cloudwatchEvent: 80 | # event: 81 | # source: 82 | # - "aws.ec2" 83 | # detail-type: 84 | # - "EC2 Instance State-change Notification" 85 | # detail: 86 | # state: 87 | # - pending 88 | # - cloudwatchLog: '/aws/lambda/hello' 89 | # - cognitoUserPool: 90 | # pool: MyUserPool 91 | # trigger: PreSignUp 92 | # - alb: 93 | # listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/ 94 | # priority: 1 95 | # conditions: 96 | # host: example.com 97 | # path: /hello 98 | 99 | # Define function environment variables here 100 | # environment: 101 | # variable2: value2 102 | 103 | # you can add CloudFormation resource templates here 104 | #resources: 105 | # Resources: 106 | # NewResource: 107 | # Type: AWS::S3::Bucket 108 | # Properties: 109 | # BucketName: my-new-bucket 110 | # Outputs: 111 | # NewOutput: 112 | # Description: "Description for the output" 113 | # Value: "Some output value" 114 | -------------------------------------------------------------------------------- /server/pocket-http.js: -------------------------------------------------------------------------------- 1 | const tracer = require('./logger')() 2 | const axios = require('axios') 3 | 4 | const client = axios.create({ 5 | baseURL: 'https://getpocket.com/v3', 6 | headers: { 7 | 'Content-Type': 'application/json; charset=UTF-8', 8 | 'X-Accept': 'application/json' 9 | } 10 | }) 11 | 12 | client.interceptors.response.use(response => { 13 | return response 14 | }, error => { 15 | const response = error.response 16 | if (response) { 17 | const config = response.config 18 | const message = `${response.status} ${config.method} ${config.url}` 19 | tracer.warn('ignoring this "error" from Pocket API') //TODO use emit 20 | tracer.warn(message) //TODO use emit 21 | // console.error(config) 22 | } else { 23 | tracer.error('Network Error') 24 | } 25 | return response 26 | }) 27 | 28 | module.exports = client 29 | -------------------------------------------------------------------------------- /server/pocket-proxy-server.js: -------------------------------------------------------------------------------- 1 | const webtask = require('webtask-tools') 2 | const tracer = require('./logger')() 3 | const secrets = module.webtask ? module.webtask.secrets : process.env 4 | process.on('unhandledRejection', (reason, p) => { 5 | const response = reason.response 6 | if (response) { 7 | const config = response.config 8 | const message = `${response.status} ${config.method} ${config.url} ${config.data}` 9 | tracer.error(message) 10 | } else { tracer.error(reason) } 11 | }) 12 | const express = require('express') 13 | // const app = require('express-async-await')(express()) 14 | const app = express() 15 | app.use(express.urlencoded({extended: true})) 16 | app.use(express.json()) 17 | 18 | app.get('/', (req, res) => { 19 | res.json({name: 'pocket-cli proxy server'}) 20 | }) 21 | 22 | const consumerKey = secrets.POCKET || new Error('Pocket consumer_key undefined') 23 | const pocketApi = require('./pocket-http') 24 | 25 | app.all('/*', async (req, res) => { 26 | const payload = Object.assign({consumer_key: consumerKey}, req.body) 27 | const response = await pocketApi.post(req.url, payload) 28 | // tracer.info(response.status, typeof response.status) 29 | // tracer.info(response.data) 30 | res.status(response.status).json(response.data) 31 | }) 32 | 33 | module.exports = webtask.fromExpress(app) 34 | -------------------------------------------------------------------------------- /snap/snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: pocket-cli 2 | version: git 3 | # notes: open source https://npmjs.com/package/pocket-cli 4 | summary: Pocket CLI - Unofficial client for https://getpocket.com 5 | description: > 6 | Start:- 7 | $ pocket-cli 8 | List all 9 | > list 10 | Search for keyword 11 | > list bitcoin 12 | > bitcoin 13 | All commands 14 | > help 15 | 16 | base: core18 17 | confinement: devmode 18 | 19 | parts: 20 | main: 21 | plugin: nodejs 22 | source: . 23 | nodejs-version: 12 24 | nodejs-package-manager: npm 25 | # node-engine: 12 26 | 27 | apps: 28 | PocketCLI: 29 | command: bin/pocket-cli -------------------------------------------------------------------------------- /src/arrays-utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | intersection: arrays => arrays.reduce((a, b) => a.filter(c => b.includes(c))), 3 | 4 | reverseIntersection: arrays => arrays.reduce((a, b) => a.filter(c => !b.includes(c))) 5 | } 6 | -------------------------------------------------------------------------------- /src/auth.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const homedir = require('os').homedir() 3 | 4 | const auth = config => { 5 | 6 | const actualConfig = config || {} 7 | // const accessTokenFileName = actualConfig.tokenFileName || 'pocket_access_token' 8 | const accessTokenPath = actualConfig.tokenFilePath 9 | ? actualConfig.tokenFilePath 10 | : `${homedir}/.config/pocket_access_token` 11 | 12 | const readToken = () => { 13 | try { 14 | const file = fs.readFileSync(accessTokenPath) 15 | return file.toString() 16 | } catch (e) { 17 | return null 18 | } 19 | } 20 | 21 | return { 22 | 23 | accessTokenPath: accessTokenPath, 24 | 25 | get: () => { 26 | const token = readToken() 27 | return token ? {access_token: readToken()} : null 28 | }, 29 | 30 | clear: () => { 31 | try { 32 | fs.unlinkSync(accessTokenPath) 33 | } catch(e) { 34 | // 35 | } 36 | } 37 | } 38 | 39 | } 40 | 41 | module.exports = auth 42 | -------------------------------------------------------------------------------- /src/cli/cli.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline') 2 | const {blue} = require('colorette') 3 | 4 | const cliProcessor = require('./process-input') 5 | const completer = require('./completer') 6 | 7 | const cli = {} 8 | 9 | const defaultPrompt = `${blue('Pocket')}> ` 10 | const ui = readline.createInterface({ 11 | input: process.stdin, 12 | output: process.stdout, 13 | completer: completer, 14 | prompt: defaultPrompt 15 | }) 16 | 17 | const platform = require('os').platform() 18 | const loader = require('./loader') 19 | 20 | cli.init = () => { 21 | console.log(`${'Pocket CLI'} - ${platform}`) 22 | ui.prompt() 23 | ui.on('line', async string => { 24 | cliProcessor.processInput(string) 25 | .then(response => { 26 | loader.stop() 27 | for (const line of response.lines || []) { 28 | console.log(line) 29 | } 30 | ui.setPrompt(response.prompt ? response.prompt : defaultPrompt) 31 | }) 32 | .catch(err => console.error(err)) 33 | .finally(() => { ui.prompt() }) 34 | loader.start() 35 | }) 36 | ui.on('close', () => { 37 | console.log('\nGoodbye.') 38 | process.exit(0) 39 | }) 40 | } 41 | 42 | module.exports = cli 43 | -------------------------------------------------------------------------------- /src/cli/completer.js: -------------------------------------------------------------------------------- 1 | const {commands} = require('./menu') 2 | 3 | const completer = line => { 4 | if (!line) return [[], line] 5 | const completions = Object.keys(commands) 6 | const hits = completions.filter(c => c.startsWith(line)) 7 | return [hits.length ? hits : completions, line] 8 | } 9 | 10 | module.exports = completer 11 | -------------------------------------------------------------------------------- /src/cli/guide.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const url = 'https://raw.githubusercontent.com/ildella/pocket-cli/master/GUIDE.md' 3 | 4 | const commands = require('./menu').commands 5 | 6 | const parse = () => ({ 7 | name: 'guide', 8 | execute: async () => { 9 | const response = await axios(url) 10 | console.log(response.data) 11 | return {lines: []} 12 | } 13 | }) 14 | 15 | commands['guide'] = { 16 | name: 'guide', 17 | aliases: [], 18 | description: 'Print this guide', 19 | parse: parse 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/help.js: -------------------------------------------------------------------------------- 1 | const commands = require('./menu').commands 2 | 3 | const output = [] 4 | 5 | // TODO: support sort alphabetical or native 6 | const parse = params => ({ 7 | name: 'help', 8 | execute: () => { 9 | if (params) { 10 | const command = commands[params[0]] 11 | if (command) { 12 | return {lines: [command.guide]} 13 | } 14 | } 15 | return {lines: output} 16 | } 17 | }) 18 | 19 | commands['help'] = { 20 | name: 'help', 21 | aliases: ['h', '?'], 22 | description: 'Print this help', 23 | parse: parse 24 | } 25 | const usage = ` 26 | Type any text and I will search for it. 27 | 28 | Type: "help {command}" to display the help for the specific command 29 | ` 30 | output.push(usage) 31 | output.push('Commands:') 32 | output.push('') 33 | Object.keys(commands).forEach(name => { 34 | output.push(` ${name} (${commands[name].aliases}) - ${commands[name].description}`) 35 | }) 36 | // output.push('') 37 | // output.push('Type "? [command]" to get more information about a command') 38 | output.push('') 39 | -------------------------------------------------------------------------------- /src/cli/interpreter.js: -------------------------------------------------------------------------------- 1 | /* 2 | Which command will take control of the user input 3 | */ 4 | 5 | const menu = require('./menu') 6 | const nullCommand = { 7 | name: 'null', 8 | aliases: [], 9 | description: 'Do nothing', 10 | parse: () => ({ 11 | execute: () => ({lines: []}) 12 | }) 13 | } 14 | 15 | const isValidString = string => typeof string == 'string' && string.trim().length > 0 ? string.trim() : false 16 | 17 | const getAction = (command, input) => ({ 18 | command: command, 19 | input: input, 20 | parse: () => command.parse(input) 21 | }) 22 | 23 | const createNullAction = () => getAction(nullCommand, '') 24 | 25 | const createAnswer = (question, inputText) => { 26 | const commandIndex = Number(inputText ? inputText : '1') 27 | const selectionIndex = question.input 28 | const command = menu.commands[question.parse().getCommand(commandIndex)] 29 | return getAction(command, selectionIndex) 30 | } 31 | 32 | const defaultCommand = 'list' 33 | 34 | const createBasicAction = spaceSeparatedInput => { 35 | const firstWord = spaceSeparatedInput[0] 36 | // TODO: finally we have a place for this function: menu 37 | const candidates = Object.values(menu.commands).filter(command => { 38 | const matches = new Set(command.aliases) 39 | matches.add(command.name) 40 | return matches.has(firstWord) 41 | }) 42 | const useDefault = candidates.length === 0 43 | // TODO: default command should not be here 44 | const command = useDefault ? menu.commands[defaultCommand] : candidates[0] 45 | // TOFIX: isInteractive should not be here, find new way to ask for isFirstWordACommand 46 | const isInteractive = command.type === 'interactive' 47 | const isFirstWordACommand = !useDefault && !isInteractive 48 | const input = isFirstWordACommand ? spaceSeparatedInput.slice(1) : spaceSeparatedInput.slice(0) 49 | return getAction(command, input) 50 | } 51 | 52 | /* eslint-disable no-use-before-define */ 53 | const createAction = inputText => { 54 | if (interpreter.question) { return createAnswer(interpreter.question, inputText) } 55 | const validString = isValidString(inputText) 56 | if (!validString) return createNullAction() 57 | return createBasicAction(validString.split(' ')) 58 | } 59 | /* eslint-enable no-use-before-define */ 60 | 61 | const interpreter = inputText => { 62 | const action = createAction(inputText) 63 | const command = action.command 64 | const isInteractive = command.type === 'interactive' 65 | if (isInteractive) { 66 | interpreter.question = action 67 | } else { 68 | interpreter.question = null 69 | } 70 | return action 71 | } 72 | 73 | module.exports = interpreter 74 | -------------------------------------------------------------------------------- /src/cli/loader.js: -------------------------------------------------------------------------------- 1 | const values = ['\\', '|', '/', '|'] 2 | 3 | const getValue = index => new Promise(resolve => { 4 | setTimeout(() => resolve(values[index]), 250) 5 | }) 6 | 7 | const asyncGenerator = async function* () { 8 | let index = 0 9 | while(true) { 10 | index++ 11 | const value = await getValue(index % values.length) 12 | yield value 13 | } 14 | } 15 | 16 | const loader = { 17 | 18 | loading: false, 19 | 20 | stop: () => { 21 | loader.loading = false 22 | process.stdout.clearLine() 23 | }, 24 | 25 | start: async () => { 26 | loader.loading = true 27 | for await (const v of asyncGenerator()) { 28 | if (loader.loading) { 29 | process.stdout.clearLine() 30 | process.stdout.cursorTo(0) 31 | process.stdout.write(v) 32 | } else { 33 | break 34 | } 35 | } 36 | } 37 | } 38 | 39 | module.exports = loader 40 | -------------------------------------------------------------------------------- /src/cli/menu.js: -------------------------------------------------------------------------------- 1 | const commands = {} 2 | let availableCommands = {} 3 | 4 | module.exports = { 5 | get: name => availableCommands[name], 6 | change: commands => availableCommands = Object.assign({}, commands), 7 | size: () => Object.entries(availableCommands).length, 8 | commands: commands 9 | } 10 | -------------------------------------------------------------------------------- /src/cli/open.js: -------------------------------------------------------------------------------- 1 | const os = require('os') 2 | 3 | const openCommands = { 4 | linux: 'xdg-open', 5 | darwin: 'open', 6 | win32: 'start' 7 | } 8 | 9 | const open = { 10 | get: (system = os.platform()) => { 11 | const command = openCommands[system] 12 | // console.log(`open command for ${system}: ${command}`) 13 | return command 14 | } 15 | } 16 | 17 | module.exports = open 18 | -------------------------------------------------------------------------------- /src/cli/process-input.js: -------------------------------------------------------------------------------- 1 | /* 2 | Control the whole flow: parse input, execute action, render result 3 | */ 4 | 5 | const {yellow, red} = require('colorette') 6 | 7 | const interpreter = require('./interpreter') 8 | const menu = require('./menu') 9 | 10 | const handleError = name => { 11 | switch (name) { 12 | case 'auth': 13 | return [yellow(`User not authenticated. Please type '${menu.commands.login.name}' to connect your Pocket account`)] 14 | case 'update': 15 | return [yellow('Please update the app')] 16 | default: 17 | return [red(`big error: ${name}`)] 18 | } 19 | } 20 | 21 | const processor = { 22 | 23 | processInput: async string => { 24 | try { 25 | const action = interpreter(string) 26 | const query = action.parse() 27 | const results = await query.execute() 28 | // menu.change(Object.assign({}, commands, action.command.submenu)) 29 | Object.assign(menu.commands, action.command.submenu) 30 | return query.render ? query.render(results) : results 31 | } catch(e) { 32 | console.log(e) 33 | return {lines: handleError(e.name)} 34 | } 35 | }, 36 | } 37 | 38 | module.exports = processor 39 | -------------------------------------------------------------------------------- /src/cli/quit.js: -------------------------------------------------------------------------------- 1 | const commands = require('./menu').commands 2 | 3 | const parse = () => ({ 4 | name: 'quit', 5 | execute: () => { 6 | console.log('Goodbye.') 7 | process.exit(0) 8 | } 9 | }) 10 | 11 | commands['quit'] = { 12 | name: 'quit', 13 | aliases: ['exit', 'q'], 14 | description: 'Quit and terminate application', 15 | parse: parse 16 | } 17 | -------------------------------------------------------------------------------- /src/cli/update.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable sonarjs/no-small-switch */ 2 | const axios = require('axios') 3 | const url = 'https://raw.githubusercontent.com/ildella/pocket-cli/master/RELEASES.md' 4 | 5 | const {commands} = require('./menu') 6 | const update = require('../npm-update') 7 | 8 | const output = [] 9 | 10 | commands['update'] = { 11 | name: 'update', 12 | aliases: ['u'], 13 | description: 'Pocket CLI update', 14 | parse: options => { 15 | const subcommand = options && options.length > 0 ? options[0] : null 16 | switch (subcommand) { 17 | case 'whatsnew': 18 | return { 19 | name: 'update-release', 20 | execute: async () => { 21 | const response = await axios(url) 22 | console.log(response.data) 23 | return {lines: output} 24 | } 25 | } 26 | default: 27 | return { 28 | name: 'update', 29 | execute: async () => { 30 | const check = await update.check() 31 | const output = [`Running: ${check.actual}. Last: ${check.last}. Need to update: ${check.need}`] 32 | if (check.need) { 33 | output.push('To update, "quit" the app then run:') 34 | output.push('npm i -g pocket-cli') 35 | } 36 | return {lines: output} 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/cli/version.js: -------------------------------------------------------------------------------- 1 | const commands = require('./menu').commands 2 | 3 | const pjson = require('../../package.json') 4 | 5 | const output = [] 6 | output.push(`${pjson.name} ${pjson.version} - ${pjson.description}`) 7 | 8 | const parse = () => ({ 9 | name: 'version', 10 | execute: () => ({lines: output}) 11 | }) 12 | 13 | commands['version'] = { 14 | name: 'version', 15 | aliases: ['v'], 16 | description: 'Print application version', 17 | parse: parse 18 | } 19 | -------------------------------------------------------------------------------- /src/commons-execute.js: -------------------------------------------------------------------------------- 1 | const {exec} = require('child_process') 2 | 3 | const cliOpen = require('./cli/open') 4 | 5 | module.exports = { 6 | open: selected => { 7 | selected.forEach(article => { 8 | exec(`${cliOpen.get()} "${article.url}"`) 9 | }) 10 | return {lines: []} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/commons-formatter.js: -------------------------------------------------------------------------------- 1 | const capFirst = string => `${string.charAt(0).toUpperCase()}${string.slice(1)}` 2 | 3 | module.exports = { 4 | 5 | toSingleLine: options => Object.keys(options) 6 | .reduce( 7 | (previousValue, currentValue, currentIndex) => { 8 | const label = options[currentValue] 9 | const defaultText = currentIndex === 0 ? ' (default)' : '' 10 | return `${previousValue} ${currentValue}. ${capFirst(label)}${defaultText}` 11 | } 12 | , '' 13 | ).trim() 14 | } 15 | -------------------------------------------------------------------------------- /src/highland-utils.js: -------------------------------------------------------------------------------- 1 | const highland = require('highland') 2 | const {promisify} = require('util') 3 | 4 | const wrapPromise = p => highland.wrapCallback(async (input, callback) => { 5 | try { 6 | const result = await p(input) 7 | callback(null, result) 8 | } catch(e) { callback(e) } 9 | }) 10 | 11 | const empty = () => ({}) 12 | const accumulate = transform => input => ({...input, ...transform(input)}) 13 | 14 | highland.wrapPromise = wrapPromise 15 | 16 | const waitCallback = (millisecs, cb) => { 17 | setTimeout(() => { cb() }, millisecs) 18 | } 19 | 20 | module.exports = { 21 | wrapPromise, 22 | highland, 23 | empty, 24 | accumulate, 25 | wait: promisify(waitCallback) 26 | } 27 | -------------------------------------------------------------------------------- /src/history.js: -------------------------------------------------------------------------------- 1 | /* 2 | A convenient class to let user access history of elements 3 | Stores timestamp of when elements has been added 4 | Interpret index in the human-readable way, starting from one 5 | */ 6 | 7 | const {DateTime} = require('luxon') 8 | 9 | const history = function (name) { 10 | let inMemory = [] 11 | const history = { 12 | name: name, 13 | add: item => { 14 | inMemory.push({ 15 | timestamp: DateTime.local().ts, 16 | item: item 17 | }) 18 | }, 19 | get: index => { 20 | if (!history.hasIndex(index)) { 21 | throw new Error(`Index ${index} does not exists in ${history.name}`) 22 | } 23 | return Object.assign({}, inMemory[Number(index) - 1].item) 24 | }, 25 | last: () => history.get(inMemory.length), 26 | hasIndex: index => { 27 | if (index <= 0) { throw new Error('Accepts only indexes > 0')} 28 | return inMemory.length >= index 29 | }, 30 | addAll: newItems => { 31 | if (!Array.isArray(newItems)) { throw new Error('addAll requires an Array')} 32 | inMemory = [] 33 | newItems.forEach(item => history.add(item)) 34 | }, 35 | size: () => inMemory.length 36 | } 37 | return history 38 | } 39 | 40 | module.exports = history 41 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | process.title = 'pocket-cli' 2 | const cli = require('./cli/cli') 3 | require('./pocket/pocket-commands') 4 | require('./cli/update') 5 | require('./cli/version') 6 | require('./cli/quit') 7 | require('./cli/help') 8 | // require('./pocket/load') 9 | 10 | const app = {} 11 | 12 | app.init = () => { 13 | setTimeout(async () => { 14 | cli.init() 15 | }, 50) 16 | } 17 | 18 | process.on('SIGINT', () => { 19 | console.log('SIGINT') 20 | process.exit(0) 21 | }) 22 | 23 | process.on('SIGTERM', () => { 24 | console.log('SIGTERM') 25 | process.exit(0) 26 | }) 27 | 28 | module.exports = app 29 | -------------------------------------------------------------------------------- /src/local-articles.js: -------------------------------------------------------------------------------- 1 | const history = require('./history') 2 | 3 | const localArticles = history('articles') 4 | 5 | module.exports = { 6 | store: articles => localArticles.addAll(articles), 7 | get: index => localArticles.get(index) 8 | } 9 | -------------------------------------------------------------------------------- /src/npm-update.js: -------------------------------------------------------------------------------- 1 | const registry = require('axios').create({baseURL: 'https://registry.npmjs.org'}) 2 | const packageJson = require('../package.json') 3 | const semver = require('semver') 4 | 5 | const update = { 6 | 7 | lastVersion: async () => { 8 | const response = await registry.get('/pocket-cli') 9 | return response.data['dist-tags'].latest 10 | }, 11 | 12 | check: async version => { 13 | const lastVersion = await update.lastVersion() 14 | const actual = version || packageJson.version 15 | const need = semver.lt(actual, lastVersion) 16 | return { 17 | need: need, 18 | actual: actual, 19 | last: lastVersion 20 | } 21 | } 22 | 23 | } 24 | module.exports = update 25 | -------------------------------------------------------------------------------- /src/pocket/article-formatter.js: -------------------------------------------------------------------------------- 1 | const {green, magenta, cyan, yellow} = require('colorette') 2 | const {DateTime} = require('luxon') 3 | 4 | const renderArticle = (entry, index, maxColumns) => { 5 | const indexOutput = `${index || entry.index || 'NA'}` 6 | const margin1 = ' '.repeat(3 - indexOutput.length) 7 | const url = entry.shortUrl.replace('http', 'https').replace('https://', '') 8 | const urlOutput = `[${url.substring(0, url.indexOf('/'))}]` 9 | const titleOutput = entry.title.substring(0, maxColumns / 2 - 10) 10 | const margin2 = ' '.repeat(5) 11 | const excerptOutput = entry.excerpt.substring(0, maxColumns - 15) 12 | const timeAdded = DateTime.fromMillis(Number(entry.time_added) * 1000).toISODate() 13 | const contentType = entry.isArticle ? 'Article' : 'Web Page' 14 | const archived = entry.isArchived ? '(A) ' : '' 15 | const favorite = entry.isFavorite ? '(*) ' : '' 16 | const details = `(${contentType}, ${entry.word_count} words)` 17 | const tags = Object.keys(entry.tags || []).join(', ') 18 | return ` ${cyan(indexOutput)}.${margin1}${archived}${favorite}${green(titleOutput)} ${yellow(urlOutput)} ${cyan(timeAdded)} ${magenta(details)} [${tags}]\n${margin2}${excerptOutput}\n` 19 | } 20 | 21 | module.exports = renderArticle 22 | -------------------------------------------------------------------------------- /src/pocket/articles-formatter.js: -------------------------------------------------------------------------------- 1 | const {green} = require('colorette') 2 | const {DateTime} = require('luxon') 3 | const maxColumns = process.stdout.columns || 100 4 | 5 | const renderArticle = require('./article-formatter') 6 | 7 | module.exports = articles => { 8 | const output = [] 9 | let index = 0 10 | for (const entry of articles) { 11 | index++ 12 | output.push(renderArticle(entry, index, maxColumns)) 13 | } 14 | return output 15 | } 16 | 17 | module.exports.toHumanText = query => { 18 | const date = DateTime.fromMillis(query.since * 1000).toLocaleString({month: 'long', day: 'numeric', year: 'numeric'}) 19 | const searchString = green(query.search || '*') 20 | const orderBy = green(query.sort) 21 | const state = green(query.state) 22 | return `Search for ${searchString} in ${state} documents, order by ${orderBy} starting ${date}` 23 | } 24 | -------------------------------------------------------------------------------- /src/pocket/pocket-auth.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const http = require('http') 3 | const {execSync} = require('child_process') 4 | const homedir = require('os').homedir() 5 | 6 | const port = process.env.CALLBACK_PORT || 3300 7 | const task = process.env.TASK || 'pocket-proxy-server-dev' 8 | const redirectURI = `http://localhost:${port}/oauth/pocket/callback` 9 | 10 | const open = require('../cli/open') 11 | const auth = require('../auth')() 12 | const client = require('./pocket-sdk')({taskName: task}) 13 | 14 | const authorize = async requestToken => { 15 | // TODO authorize should return the code, not the whole response 16 | const response = await client.authorize(requestToken) 17 | // TODO: use auth.write instead of direct fs write 18 | fs.writeFileSync(`${homedir}/.config/pocket_access_token`, `${response.data.access_token}\n`) 19 | return response.data.username 20 | } 21 | 22 | const simpleServer = async requestToken => { 23 | const server = http.createServer(async (req, res) => { 24 | if (req.url === '/' && req.method === 'GET') { 25 | res.end('up and running\n') 26 | } 27 | if (req.url === '/oauth/pocket/callback' && req.method === 'GET') { 28 | const username = await authorize(requestToken) 29 | res.writeHead(200, {'Content-Type': 'text/plain'}) 30 | res.end(`Authentication completed, welcome '${username}'. You can close this tab.`) 31 | server.close(err => { 32 | if (err) { 33 | console.error(err) 34 | process.exit(1) 35 | } 36 | // console.log('Server closed with no errors. Shutting down connections') 37 | }) 38 | } 39 | res.writeHead(400, {'Content-Type': 'text/plain'}) 40 | res.end('Do not handle that address\n') 41 | }) 42 | const listener = await server.listen(process.env.CALLBACK_PORT || 3300) 43 | // console.log(`Auth Callback Server started -> http://localhost:${listener.address().port}`) 44 | } 45 | 46 | const pocketAuth = {} 47 | 48 | pocketAuth.login = async () => { 49 | // TODO should return the authorizeUrl 50 | const requestToken = await client.requestToken(redirectURI) 51 | const authorizeUrl = `https://getpocket.com/auth/authorize?request_token=${requestToken}&redirect_uri=${redirectURI}` 52 | await simpleServer(requestToken) 53 | const exec = execSync(`${open.get()} "${authorizeUrl}"`) 54 | } 55 | 56 | pocketAuth.logout = () => { 57 | auth.clear() 58 | } 59 | 60 | module.exports = pocketAuth 61 | -------------------------------------------------------------------------------- /src/pocket/pocket-commands.js: -------------------------------------------------------------------------------- 1 | const {commands} = require('../cli/menu') 2 | const pocketParse = require('./pocket-parse') 3 | 4 | const listCommands = { 5 | archive: { 6 | name: 'archive', 7 | aliases: ['a', 'read', 'r'], 8 | description: 'Archive article / Mark as read', 9 | parse: pocketParse.archive 10 | }, 11 | delete: { 12 | name: 'delete', 13 | aliases: ['d'], 14 | description: 'Delete article (permanently)', 15 | parse: pocketParse.delete 16 | }, 17 | favorite: { 18 | name: 'favorite', 19 | aliases: ['fav', 'f'], 20 | description: 'Favorite article', 21 | parse: pocketParse.favorite 22 | }, 23 | tag: { 24 | name: 'tag', 25 | aliases: ['t'], 26 | description: 'Tag article', 27 | parse: pocketParse.tag 28 | }, 29 | readd: { 30 | name: 'readd', 31 | aliases: ['unarchive'], 32 | description: 'Un-archive article / Mark as unread', 33 | parse: pocketParse.readd 34 | }, 35 | // print: { 36 | // name: 'print', 37 | // aliases: ['p'], 38 | // description: 'print last search results', 39 | // parse: pocketParse.print 40 | // }, 41 | next: { 42 | name: 'next', 43 | aliases: ['n'], 44 | description: 'next set of results', 45 | parse: pocketParse.next 46 | }, 47 | previous: { 48 | name: 'previous', 49 | aliases: ['p'], 50 | description: 'previous set of results', 51 | parse: pocketParse.previous 52 | }, 53 | expand: { 54 | name: 'expand', 55 | aliases: ['e'], 56 | description: 'print the whole excerpt', 57 | parse: pocketParse.expand 58 | }, 59 | open: { 60 | name: 'open', 61 | aliases: ['o'], 62 | description: 'open the URL in the browser', 63 | parse: pocketParse.open 64 | }, 65 | select: { 66 | type: 'interactive', 67 | name: 'select', 68 | aliases: ['1', '2', '3', '4', '5', '6', '7', '8'], 69 | description: 'interactive action on a listed item (eg: archive, fav, tag...)', 70 | parse: pocketParse.select 71 | } 72 | } 73 | 74 | commands['login'] = { 75 | name: 'login', 76 | aliases: ['signin', 'auth'], 77 | description: 'Login to Pocket', 78 | parse: pocketParse.login 79 | } 80 | commands['logout'] = { 81 | name: 'logout', 82 | aliases: ['signout'], 83 | description: 'Logout from Pocket', 84 | parse: pocketParse.logout 85 | } 86 | commands['list'] = { 87 | name: 'list', 88 | aliases: ['ls', 'l', 'search', 's', 'find'], 89 | description: 'Search for the given keywords. eg: list bitcoin', 90 | guide: 91 | ` 92 | List is the default command. 93 | If no other command is detected, I search for the input text. 94 | 95 | Available parameters: 96 | 97 | unread search only for non-archived articles 98 | oldest / newest list starting from oldest / newest 99 | 100 | `, 101 | submenu: listCommands, 102 | parse: pocketParse.list 103 | } 104 | commands['mode'] = { 105 | name: 'mode', 106 | aliases: ['m', 'filters'], 107 | description: 'Enter a specific mode that can act as a filter', 108 | guide: 109 | ` 110 | Usage: 111 | mode unread --> all future list requests will use this filter 112 | `, 113 | parse: pocketParse.mode 114 | } 115 | -------------------------------------------------------------------------------- /src/pocket/pocket-execute.js: -------------------------------------------------------------------------------- 1 | const auth = require('../auth')() 2 | 3 | const task = process.env.TASK || 'pocket-proxy-server-dev' 4 | 5 | const orderByDesc = key => (a, b) => a[key] > b[key] ? -1 : b[key] > a[key] ? 1 : 0 6 | 7 | const pocket = { 8 | 9 | modify: async actions => { 10 | const client = require('./pocket-sdk')({ 11 | taskName: task, 12 | auth: auth.get() 13 | }) 14 | const response = await client.modify(actions) 15 | const results = response.data.action_results.map(result => result ? 'success' : 'failed') 16 | return {lines: [`${results}`]} 17 | }, 18 | 19 | retrieve: async search => { 20 | const client = require('./pocket-sdk')({ 21 | taskName: task, 22 | auth: auth.get() 23 | }) 24 | const response = await client.retrieve(search) 25 | const articles = Object.values(response.data.list) 26 | const parsedArticles = articles.map(article => { 27 | const id = article.resolved_id || article.item_id || '' 28 | const authors = Object.values(article.authors || {}).map(author => author.name) 29 | const title = article.resolved_title || id || '' 30 | const url = article.resolved_url || article.given_url || '' 31 | const shortUrl = url.replace('https://', '').replace('http://', '') 32 | const isArticle = article.is_article === 1 33 | const isArchived = article.status == 1 34 | const isFavorite = article.favorite == 1 35 | const excerpt = article.excerpt || '' 36 | const newFields = { 37 | id: id, 38 | authors: authors, 39 | title: title, 40 | url: url, 41 | shortUrl: shortUrl, 42 | isArticle: isArticle, 43 | isArchived: isArchived, 44 | isFavorite: isFavorite, 45 | excerpt: excerpt 46 | } 47 | return Object.assign({}, article, newFields) 48 | }) 49 | parsedArticles.sort(orderByDesc('time_added')) 50 | return parsedArticles 51 | } 52 | } 53 | 54 | module.exports = pocket 55 | -------------------------------------------------------------------------------- /src/pocket/pocket-parse.js: -------------------------------------------------------------------------------- 1 | const {green, gray, cyan, blue, yellow} = require('colorette') 2 | const {DateTime, Settings} = require('luxon') 3 | Settings.defaultZoneName = 'utc' 4 | 5 | const {intersection, reverseIntersection} = require('../arrays-utils') 6 | const {open} = require('../commons-execute') 7 | const history = require('../history') 8 | 9 | const pocketAuth = require('./pocket-auth') 10 | const pocketExecute = require('./pocket-execute') 11 | 12 | const states = ['unread', 'archive'] 13 | const orders = ['newest', 'oldest', 'title', 'site'] 14 | 15 | const queries = history('queries') 16 | const localArticles = require('../local-articles') 17 | const defaultSearch = { 18 | count: 8, 19 | offset: 0, 20 | detailType: 'complete', 21 | since: 0 22 | } 23 | 24 | const indexes = cyan('1-8') 25 | const command1 = cyan('open 1') 26 | const command2 = cyan('archive 2') 27 | const listGuide = gray(`Type ${indexes} to select an index or isuse commands like ${command1} and ${command2}. Press TAB to show commands`) 28 | const noResultsGuide = yellow('No results found') 29 | 30 | const formatter = require('./articles-formatter') 31 | const {toHumanText} = require('./articles-formatter') 32 | const {toSingleLine} = require('../commons-formatter') 33 | 34 | const render = (search, articles) => { 35 | const renderedArticles = formatter(articles) 36 | const output = [''].concat(renderedArticles) 37 | const guide = articles.length > 0 ? listGuide : noResultsGuide 38 | const leftMargin = ' '.repeat(5) 39 | output.push(`${leftMargin}${blue(toHumanText(search))}`) 40 | output.push(`${leftMargin}${guide}`) 41 | output.push('') 42 | return {lines: output} 43 | } 44 | 45 | const options = isArchived => ({ 46 | '1': 'open', 47 | '2': 'expand', 48 | '3': 'favorite', 49 | '4': isArchived ? 'readd' : 'archive', 50 | '5': 'delete', 51 | }) 52 | 53 | const retrieve = async search => { 54 | queries.add(search) 55 | const retrievedArticles = await pocketExecute.retrieve(search) 56 | localArticles.store(retrievedArticles) 57 | return retrievedArticles 58 | } 59 | 60 | const actionsHistory = [] 61 | 62 | const modify = ({action, custom = {}, indexes}) => { 63 | const matches = indexes.flat().map(i => localArticles.get(i)).filter(Boolean) 64 | const actions = matches.map(article => { 65 | const base = { 66 | action: action, 67 | item_id: article.item_id, 68 | time: DateTime.local().ts / 1000 69 | } 70 | return {...base, ...custom} 71 | }) 72 | return { 73 | // name: `pocket-modify-${action}`, 74 | name: 'pocket-modify', 75 | actions: actions, 76 | execute: () => { 77 | actionsHistory.push({ 78 | timestamp: DateTime.local(), 79 | actions: actions 80 | }) 81 | return pocketExecute.modify(actions) 82 | } 83 | } 84 | } 85 | 86 | const pocketParse = { 87 | 88 | login: () => ({ 89 | name: 'pocket-auth', 90 | execute: async () => { 91 | await pocketAuth.login() 92 | return {lines: [green('User logged in')]} 93 | } 94 | }), 95 | 96 | logout: () => ({ 97 | name: 'pocket-auth', 98 | execute: async () => { 99 | pocketAuth.logout() 100 | return {lines: [green('User logged out')]} 101 | } 102 | }), 103 | 104 | archive: (...indexes) => modify({action: 'archive', indexes: indexes}), 105 | 106 | delete: indexes => modify({action: 'delete', indexes: indexes}), 107 | 108 | favorite: indexes => modify({action: 'favorite', indexes: indexes}), 109 | 110 | readd: indexes => modify({action: 'readd', indexes: indexes}), 111 | 112 | tag: inputs => { 113 | // console.log('inputs', inputs) 114 | const allIndexes = ['1', '2', '3', '4', '5', '6', '7', '8'] 115 | const tags = reverseIntersection([inputs, allIndexes]).join(', ') 116 | const indexes = intersection([inputs, allIndexes]) 117 | return modify({action: 'tags_add', custom: {tags: tags}, indexes: indexes}) 118 | }, 119 | 120 | expand: indexes => { 121 | const index = indexes[0] 122 | const selected = localArticles.get(index) 123 | return { 124 | name: 'pocket-expand', 125 | indexes: indexes, 126 | index: index, 127 | execute: () => { 128 | const output = [] 129 | output.push(blue(selected.excerpt)) 130 | return {lines: output} 131 | } 132 | } 133 | }, 134 | 135 | open: indexes => { 136 | const selected = indexes.map(index => localArticles.get(index)) 137 | return { 138 | name: 'pocket-open', 139 | indexes: indexes, 140 | execute: () => open(selected) 141 | } 142 | }, 143 | 144 | next: () => { 145 | const search = queries.size() > 0 ? queries.last() : defaultSearch 146 | // console.log(search) 147 | search.offset = search.offset + search.count 148 | return { 149 | name: 'pocket-next', 150 | search: search, 151 | execute: () => retrieve(search), 152 | render: articles => render(search, articles) 153 | } 154 | }, 155 | 156 | previous: () => { 157 | const search = queries.last() 158 | search.offset = search.offset - search.count 159 | return { 160 | name: 'pocket-next', 161 | search: search, 162 | execute: () => retrieve(search), 163 | render: articles => render(search, articles) 164 | } 165 | }, 166 | 167 | list: (inputs = []) => { 168 | const reservedState = intersection([states, inputs]) 169 | const reservedOrder = intersection([orders, inputs]) 170 | const state = reservedState.length > 0 ? reservedState[0] : 'all' 171 | const order = reservedOrder.length > 0 ? reservedOrder[0] : 'newest' 172 | const params = reverseIntersection([inputs, states, orders]) 173 | const search = Object.assign({}, { 174 | sort: order, 175 | state: state, 176 | search: params.toString().replace(',', ' '), 177 | // since: DateTime.local().startOf('day').minus({month: 1}).ts / 1000 178 | }, defaultSearch) 179 | return { 180 | name: 'pocket-list', 181 | search: search, 182 | execute: () => retrieve(search), 183 | render: articles => render(search, articles) 184 | } 185 | }, 186 | 187 | select: index => { 188 | const article = localArticles.get(index) 189 | const opts = options(article.isArchived) 190 | return { 191 | name: 'select-query', 192 | execute: () => ({ 193 | lines: [toSingleLine(opts)], 194 | prompt: yellow('select: ') 195 | }), 196 | getCommand: actionIndex => opts[actionIndex] 197 | } 198 | }, 199 | 200 | mode: inputs => { 201 | const modes = intersection([states, inputs]) 202 | const feedback = modes.length > 0 ? [] : [`no modes found for "${inputs}"`] 203 | return { 204 | name: 'set-mode', 205 | execute: () => ({ 206 | lines: feedback, 207 | prompt: `${blue(`Pocket:${modes}`)}> ` 208 | }) 209 | } 210 | } 211 | 212 | } 213 | 214 | module.exports = pocketParse 215 | -------------------------------------------------------------------------------- /src/pocket/pocket-sdk.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const containerName = 'wt-c7bbe7e68d36c0caa6436b2be9c7052a-0' 3 | 4 | const pocket = (client, authJson) => { 5 | 6 | const pocket = {} 7 | 8 | pocket.requestToken = async redirectURI => { 9 | const body = { 10 | redirect_uri: `${redirectURI}`, 11 | state: 'ok' 12 | } 13 | const response = await client.post('/oauth/request', body) 14 | return response.data.code 15 | } 16 | 17 | pocket.authorize = requestToken => client.post('/oauth/authorize', {code: requestToken}) 18 | 19 | pocket.retrieve = async search => { 20 | const query = Object.assign(authJson, search) 21 | return client.post('/get', query) 22 | } 23 | 24 | pocket.modify = async actions => { 25 | const query = Object.assign( 26 | authJson, 27 | {actions: actions} 28 | ) 29 | return client.post('/send', query) 30 | } 31 | 32 | return pocket 33 | 34 | } 35 | 36 | const build = config => { 37 | 38 | const actualConfig = config || {} 39 | const taskName = actualConfig.taskName || 'pocket-proxy-server-dev' 40 | const authJson = actualConfig.auth || {} 41 | 42 | const client = axios.create({ 43 | baseURL: `https://${containerName}.sandbox.auth0-extend.com/${taskName}`, 44 | headers: { 45 | 'Content-Type': 'application/json; charset=UTF-8', 46 | 'X-Accept': 'application/json' 47 | } 48 | }) 49 | 50 | client.interceptors.response.use(response => response, error => { 51 | const response = error.response 52 | if (response) { 53 | const config = response.config 54 | const message = `${response.status} ${config.method} ${config.url}` 55 | const err = new Error(message) 56 | err.name = response.status === 401 ? 'auth' : 'api-error' 57 | throw err 58 | } 59 | throw new Error('Network Error', 'network-error') 60 | }) 61 | 62 | return pocket(client, authJson) 63 | } 64 | 65 | module.exports = build 66 | -------------------------------------------------------------------------------- /src/pocket/wc.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const readline = require('readline') 3 | const {promisify} = require('util') 4 | 5 | const lines = (path, done) => { 6 | let counter = 0 7 | const rl = readline.createInterface({input: fs.createReadStream(path)}) 8 | rl.on('line', () => { counter++ }) 9 | rl.on('close', () => { 10 | done(null, counter) 11 | }) 12 | } 13 | 14 | module.exports = { 15 | lines: promisify(lines) 16 | } 17 | -------------------------------------------------------------------------------- /src/templates.js: -------------------------------------------------------------------------------- 1 | const templateFrom = (template, vars) => { 2 | const fillTemplate = function (template, vars) { 3 | return new Function('return `' + template + '`;').call(vars) 4 | } 5 | return fillTemplate(template, vars) 6 | } 7 | 8 | module.exports = templateFrom 9 | -------------------------------------------------------------------------------- /test/article-formatter.test.js: -------------------------------------------------------------------------------- 1 | const formatter = require('../src/pocket/article-formatter') 2 | 3 | test('format', () => { 4 | const entry = { 5 | time_added: '1543489802', 6 | title: 'something something dark side, and a lot of this bla bla is going to be cut', 7 | shortUrl: 'http://sub.domain.com/path/to/article', 8 | excerpt: 'NOTE: This is a draft in progress, so that I can get some feedback from early reviewers.' 9 | } 10 | const formattedText = formatter(entry, 0, 100) 11 | expect(formattedText).toContain('something something dark side,') 12 | expect(formattedText).not.toContain('of this bla bla is going to be cut') 13 | expect(formattedText).not.toContain('of') 14 | }) 15 | -------------------------------------------------------------------------------- /test/auth.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs').promises 2 | const auth = require('../src/auth')({tokenFilePath: 'test_token'}) 3 | 4 | afterAll(async () => { 5 | try { 6 | await fs.unlink(auth.accessTokenPath) 7 | } catch(e) { 8 | console.log('there is nothing bad about this...') 9 | } 10 | }) 11 | 12 | test('auth exists', async () => { 13 | expect(auth.get()).toBeNull() 14 | const token = 'abcd1234' 15 | await fs.writeFile(auth.accessTokenPath, token) 16 | const json = auth.get() 17 | expect(json).toEqual({access_token: token}) 18 | }) 19 | -------------------------------------------------------------------------------- /test/cli.test.js: -------------------------------------------------------------------------------- 1 | // const cli = require('../src/cli/cli') 2 | 3 | test('cli starts', () => { 4 | // expect(cli).toBeDefined() 5 | // TODO: figure out what's Jest problem with function* () 6 | }) 7 | -------------------------------------------------------------------------------- /test/commands.test.js: -------------------------------------------------------------------------------- 1 | const {commands} = require('../src/cli/menu') 2 | const interpreter = require('../src/cli/interpreter') 3 | require('../src/cli/quit') 4 | require('../src/cli/version') 5 | require('../src/cli/update') 6 | require('../src/cli/help') 7 | 8 | const isFunction = x => typeof x === 'function' 9 | 10 | const isAsyncFunction = f => f[Symbol.toStringTag] === 'AsyncFunction' 11 | 12 | test('call commands query', async () => { 13 | const query = commands.quit.parse() 14 | expect(query.name).toEqual('quit') 15 | expect(isFunction(query.execute)).toBeTruthy() 16 | expect(isAsyncFunction(query.execute)).toBeFalsy() 17 | }) 18 | 19 | test('help', async () => { 20 | expect(interpreter('quit').command).toBe(commands.quit) 21 | expect(interpreter('help').command).toBe(commands.help) 22 | expect(interpreter('version').command).toBe(commands.version) 23 | expect(interpreter('update').command).toBe(commands.update) 24 | }) 25 | -------------------------------------------------------------------------------- /test/completer.test.js: -------------------------------------------------------------------------------- 1 | const completer = require('../src/cli/completer') 2 | 3 | require('../src/cli/help') 4 | 5 | test('empty requests returns nothing', () => { 6 | expect(completer()).toEqual([[], undefined]) 7 | expect(completer('')).toEqual([[], '']) 8 | }) 9 | 10 | test('completer basic', () => { 11 | expect(completer('he')).toEqual([['help'], 'he']) 12 | }) 13 | -------------------------------------------------------------------------------- /test/fixtures/article-sample.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | item_id: '2412335750', 3 | resolved_id: '2412335762', 4 | given_url: 'https://example.com/path/to/article', 5 | given_title: 'Something/important: the best article on the topic', 6 | favorite: '0', 7 | status: '1', 8 | time_added: '1544141452', 9 | time_updated: '1544885262', 10 | time_read: '494000', 11 | time_favorited: '0', 12 | sort_id: 1, 13 | resolved_title: 'Something/important', 14 | resolved_url: 'https://example.com/path/to/article', 15 | excerpt: 16 | 'something something dark side', 17 | is_article: '1', 18 | is_index: '1', 19 | has_video: '0', 20 | has_image: '1', 21 | word_count: '709', 22 | lang: 'en', 23 | time_to_read: 3, 24 | top_image_url: 'https://somesite.with.images.com', 25 | tags: {vps: {item_id: '2443916156', tag: 'vps'}, brasil: {}}, 26 | image: 27 | {item_id: '2412335750', 28 | src: 'https://something.com/image.png', 29 | width: '0', 30 | height: '0'}, 31 | images: { 32 | '1': {item_id: '2412335750', 33 | image_id: '1', 34 | src: 'https://something.com/image.png', 35 | width: '0', 36 | height: '0', 37 | credit: '', 38 | caption: ''} 39 | }, 40 | domain_metadata: { 41 | name: 'GitHub', 42 | logo: 'https://logo.clearbit.com/github.com?size=800', 43 | greyscale_logo: 'https://logo.clearbit.com/github.com?size=800&greyscale=true', 44 | }, 45 | listen_duration_estimate: 274, 46 | } 47 | -------------------------------------------------------------------------------- /test/fixtures/article.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = { 3 | textContent: '\n' + 4 | ' Have a look at the part 1 for the necessary context. Here we go.\n' + 5 | 'Question: count the name occurrencesWe need to identify the most common first name in the data and how many times it occurs.Map/reduce is convenient here, the name is in the column 8, so we isolate the column than accumulate te occurrences in an object. \n' + 6 | '12345678910111213const occurrences = {}const namesOccurences = () => { __(generator) .map(line => line.split(\'|\')[8]) .reduce(0, (a, name) => { occurrences[name] = (occurrences[name] || 0) + 1 return name }) .done(() => { console.log(\'Highland> names occurrences:\') console.log(occurrences) })}\n' + 7 | 'Question: donations per monthCount how many donations occurred in each month and print out the results. The donation amount is in the column 5. \n' + 8 | 'This time we extract the reduce function for more clarity:\n' + 9 | '123456789101112131415const accumulator = {}const sumByMonth = function (a, item) { accumulator[item.month] = (accumulator[item.month] || 0) + 1 return item}const countDonationsPerMonth = () => { __(generator) .map(line => { return {month: line.split(\'|\')[4].substring(4, 6), line: line,}}) .reduce(0, sumByMonth) .done(() => { console.log(\'Highland> donations per month:\') console.log(accumulator) })}\n', 10 | excerpt: 'Have a look at the part 1 for the necessary context. Here we go. Question: count the name occurrencesWe need to identify the most common first name in the data and how many times it occurs.Map/reduce', 11 | title: 'Parse a large text file using Node.js streams', 12 | byline: 'ildella', 13 | length: 1846, 14 | dir: null, 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/mock.parsed.articles.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = [ 3 | { 4 | // from pocket 5 | id: 'm1', 6 | item_id: 'm1', 7 | given_url: 'https://example.com/path/to/article', 8 | given_title: 'Soemthing/important: the best article on the topic', 9 | title: 'Soemthing/important: the best article on the topic', 10 | authors: 'John Smith', 11 | favorite: '0', 12 | status: '1', 13 | time_added: '1544141452', 14 | time_updated: '1544885262', 15 | time_read: '494000', 16 | time_favorited: '0', 17 | sort_id: 1, 18 | resolved_title: 'Something/important', 19 | resolved_url: 'https://example.com/path/to/article', 20 | url: 'https://example.com/path/to/article', 21 | is_article: '1', 22 | is_index: '1', 23 | has_video: '0', 24 | has_image: '1', 25 | word_count: '709', 26 | lang: 'en', 27 | time_to_read: 3, 28 | // from readability 29 | textContent: '\n' + 30 | ' Have a look at the part 1 for the necessary context. Here we go.\n' + 31 | 'Question: count the name occurrencesWe need to identify the most common first name in the data and how many times it occurs.Map/reduce is convenient here, the name is in the column 8, so we isolate the column than accumulate te occurrences in an object. \n' + 32 | '12345678910111213const occurrences = {}const namesOccurences = () => { __(generator) .map(line => line.split(\'|\')[8]) .reduce(0, (a, name) => { occurrences[name] = (occurrences[name] || 0) + 1 return name }) .done(() => { console.log(\'Highland> names occurrences:\') console.log(occurrences) })}\n' + 33 | 'Question: donations per monthCount how many donations occurred in each month and print out the results. The donation amount is in the column 5. \n' + 34 | 'This time we extract the reduce function for more clarity:\n' + 35 | '123456789101112131415const accumulator = {}const sumByMonth = function (a, item) { accumulator[item.month] = (accumulator[item.month] || 0) + 1 return item}const countDonationsPerMonth = () => { __(generator) .map(line => { return {month: line.split(\'|\')[4].substring(4, 6), line: line,}}) .reduce(0, sumByMonth) .done(() => { console.log(\'Highland> donations per month:\') console.log(accumulator) })}\n', 36 | excerpt: 'Have a look at the part 1 for the necessary context. Here we go. Question: count the name occurrencesWe need to identify the most common first name in the data and how many times it occurs.Map/reduce', 37 | // title: 'Parse a large text file using Node.js streams', 38 | byline: 'ildella', 39 | length: 1846, 40 | dir: null, 41 | }, 42 | { 43 | id: 'm2', 44 | item_id: 'm2', 45 | authors: 'John Doe', 46 | excerpt: 'something something dark side', 47 | }, 48 | { 49 | id: 'm3', 50 | item_id: 'm3' 51 | }, 52 | { 53 | id: 'm4', 54 | item_id: 'm4' 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /test/history.test.js: -------------------------------------------------------------------------------- 1 | const history = require('../src/history') 2 | 3 | test('not same', () => { 4 | const h1 = history('test') 5 | const h2 = history('h2') 6 | expect(h1).not.toBe(h2) 7 | }) 8 | 9 | test('separated', () => { 10 | const h1 = history('h1') 11 | const h2 = history('h2') 12 | h1.add(1) 13 | expect(h1.size()).toBe(1) 14 | expect(h2.size()).toBe(0) 15 | }) 16 | 17 | test('init', () => { 18 | const h = history('h') 19 | expect(h.size()).toBe(0) 20 | expect(() => { h.last() }).toThrow() 21 | expect(() => { h.hasIndex(0) }).toThrow() 22 | expect(h.hasIndex(1)).toBeFalsy() 23 | }) 24 | 25 | test('add and get', () => { 26 | const h = history('h') 27 | h.add({a: 1}) 28 | expect(h.size()).toBe(1) 29 | // expect(h.hasIndex(0)).toBeTruthy() 30 | expect(h.hasIndex(1)).toBeTruthy() 31 | expect(h.hasIndex(2)).toBeFalsy() 32 | expect(h.get(1)).toEqual({a: 1}) 33 | expect(h.get(1)).toEqual(h.last()) 34 | }) 35 | 36 | test('addAll', () => { 37 | const h = history('h') 38 | h.addAll([{a: 1}, {a: 2}]) 39 | expect(h.size()).toBe(2) 40 | expect(h.last()).toEqual({a: 2}) 41 | }) 42 | -------------------------------------------------------------------------------- /test/integrations/pocket-sdk.test.js: -------------------------------------------------------------------------------- 1 | const cexpect = require('chai').expect 2 | const pocketSdk = require('../../src/pocket/pocket-sdk') 3 | 4 | test('same instance', () => { 5 | const p1 = pocketSdk() 6 | const p2 = pocketSdk() 7 | expect(p1).not.toBe(p2) 8 | }) 9 | 10 | test('read from pocket API', async () => { 11 | const auth = require('../../src/auth')() 12 | const authJson = auth.get() 13 | const pocket = pocketSdk({ 14 | taskName: 'pocket-proxy-server-dev', 15 | auth: authJson 16 | }) 17 | const search = { 18 | count: 10, 19 | offset: 0, 20 | since: 0 21 | } 22 | const response = await pocket.retrieve(search) 23 | expect(response.status).toBe(200) 24 | expect(response.data.status).toBe(1) 25 | expect(response.data.complete).toBe(1) 26 | expect(response.data.error).toBeNull() 27 | expect(response.data.search_meta).toEqual({search_type: 'normal'}) 28 | // console.log(response.data) 29 | expect(Object.values(response.data.list)).toHaveLength(10) 30 | }) 31 | 32 | test('unauthorized', async () => { 33 | const pocket = pocketSdk() 34 | const search = { 35 | count: 10, 36 | offset: 0, 37 | since: 0 38 | } 39 | 40 | try { 41 | await pocket.retrieve(search) 42 | } catch (err) { 43 | cexpect(err).to.be.an.instanceof(Error) 44 | // cexpect(err.message).to.contain('auth') 45 | cexpect(err.message).to.contain('401') 46 | } 47 | 48 | }) 49 | 50 | test('requestToken', async () => { 51 | const pocket = pocketSdk() 52 | const token = await pocket.requestToken('http://localhost:4444/callback') 53 | expect(token).toBeDefined() 54 | expect(token).toHaveLength(30) 55 | }) 56 | -------------------------------------------------------------------------------- /test/integrations/smoke.test.js: -------------------------------------------------------------------------------- 1 | const processor = require('../../src/cli/process-input') 2 | require('../../src/pocket/pocket-commands') 3 | require('../../src/cli/version') 4 | require('../../src/cli/quit') 5 | require('../../src/cli/help') 6 | 7 | test('read from pocket API', async () => { 8 | const output = await processor.processInput('bitcoin') 9 | expect(output.lines).toHaveLength(12) 10 | await processor.processInput('n') 11 | }) 12 | 13 | test('modify with pocket API - archive/readd', async () => { 14 | await processor.processInput('bitcoin') 15 | const output = await processor.processInput('a 2') 16 | expect(output.lines).toEqual(['success']) 17 | }) 18 | 19 | test('multiple archive', async () => { 20 | await processor.processInput('bitcoin') 21 | const output = await processor.processInput('a 2 3') 22 | expect(output.lines).toEqual(['success,success']) 23 | }) 24 | 25 | // test('archive and readd', async () => { 26 | // const list = await processor.processInput('bitcoin') 27 | // expect(list.lines[1]).toContain('2. (A)') 28 | // const output = await processor.processInput('2') 29 | // expect(output.lines).toContain('4. Readd') 30 | // }) 31 | -------------------------------------------------------------------------------- /test/integrations/update.test.js: -------------------------------------------------------------------------------- 1 | const update = require('../../src/npm-update') 2 | 3 | test('last version', async () => { 4 | const response = await update.lastVersion() 5 | expect(response).toBeDefined() 6 | }) 7 | 8 | test('check', async () => { 9 | const response = await update.check() 10 | expect(response.need).toBe(false) 11 | }) 12 | 13 | test('mock check', async () => { 14 | const response = await update.check('0.4.2') 15 | expect(response.need).toBeTruthy() 16 | expect(response.actual).toBe('0.4.2') 17 | }) 18 | 19 | test('mock check 2', async () => { 20 | const response = await update.check('0.25.0-pre') 21 | expect(response.need).toBeFalsy() 22 | expect(response.actual).toBe('0.25.0-pre') 23 | }) 24 | -------------------------------------------------------------------------------- /test/interpreter-pocket-list-subcommands.test.js: -------------------------------------------------------------------------------- 1 | const processor = require('../src/cli/process-input') 2 | // const interpreter = require('../src/cli/interpreter') 3 | // const {commands} = require('../src/cli/menu') 4 | require('../src/pocket/pocket-commands') 5 | 6 | beforeAll(() => { 7 | const localArticles = require('../src/local-articles') 8 | const mockArticles = require('./fixtures/mock.parsed.articles') 9 | localArticles.store(mockArticles) 10 | // expect(commands).toBe({}) 11 | // expect(commands.archive).toBeDefined() 12 | }) 13 | 14 | test('mode wrong input', async () => { 15 | const rendered = await processor.processInput('mode something') 16 | expect(rendered.lines[0]).toContain('no modes found') 17 | }) 18 | 19 | test('mode unread', async () => { 20 | const rendered = await processor.processInput('mode unread') 21 | // console.log(rendered) 22 | // TODO: check that this query has unread in it 23 | // const parsed = processor.interpreter('list').parse() 24 | // TODO: refactor so rendering happens somewhere else and we can test processor and intepreter together 25 | // TODO: remove the console.log for errors in processor, add tracer 26 | }) 27 | 28 | // test('archive', async () => { 29 | // expect(interpreter('archive 1').command).toBe(commands.archive) 30 | // expect(interpreter('a 1').command).toBe(commands.archive) 31 | // expect(interpreter('aa 1').command).toBe(commands.list) 32 | // interpreter('a 1').parse() 33 | // }) 34 | 35 | // test('archive multiple items', async () => { 36 | // const archiveMultiple = interpreter('a 1 2 3') 37 | // expect(archiveMultiple.command).toBe(commands.archive) 38 | // expect(archiveMultiple.input).toEqual(['1', '2', '3']) 39 | // }) 40 | 41 | // test('favorite multiple items', async () => { 42 | // const archiveMultiple = interpreter('fav 1 2 3') 43 | // expect(archiveMultiple.command).toBe(commands.favorite) 44 | // expect(archiveMultiple.input).toEqual(['1', '2', '3']) 45 | // }) 46 | 47 | // test('favorite without parameter', async () => { 48 | // const archiveMultiple = interpreter('fav') 49 | // expect(archiveMultiple.command).toBe(commands.favorite) 50 | // expect(archiveMultiple.input).toEqual([]) 51 | // }) 52 | 53 | // test('open', async () => { 54 | // const action = interpreter('open 1') 55 | // expect(action.command).toBe(commands.open) 56 | // const query = action.parse() 57 | // expect(query.name).toBe('pocket-open') 58 | // expect(query.indexes).toEqual(['1']) 59 | // }) 60 | 61 | // test('expand', async () => { 62 | // interpreter('list').parse() 63 | // const action = interpreter('e 3') 64 | // expect(action.command).toBe(commands.expand) 65 | // const query = action.parse() 66 | // expect(query.name).toBe('pocket-expand') 67 | // expect(query.indexes).toEqual(['3']) 68 | // }) 69 | 70 | // test('next and previous', async () => { 71 | // interpreter('list').parse() 72 | // const next = interpreter('next') 73 | // expect(next.command).toBe(commands.next) 74 | // }) 75 | 76 | // test('interactive', async () => { 77 | // interpreter('list') 78 | 79 | // const action = interpreter('1') 80 | // expect(action.command).toBe(commands.select) 81 | // expect(action.input).toEqual(['1']) 82 | // expect(action.parse).toBeDefined() 83 | // expect(interpreter.question).toBe(action) 84 | 85 | // const query = action.parse() 86 | // expect(query.name).toBe('select-query') 87 | // const output = await query.execute() 88 | // expect(output.lines).toEqual(['1. Open (default) 2. Expand 3. Favorite 4. Archive 5. Delete']) 89 | 90 | // const answerAction = interpreter('2') 91 | // expect(answerAction.command).toBe(commands.expand) 92 | // expect(interpreter.question).toBeUndefined() 93 | // const answerQuery = answerAction.parse() 94 | // expect(answerQuery.name).toBe('pocket-expand') 95 | // expect(answerQuery.index).toEqual('1') 96 | 97 | // const nextAction = interpreter('something') 98 | // expect(nextAction.command).toBe(commands.list) 99 | // expect(nextAction.input).toEqual(['something']) 100 | // expect(interpreter.question).toBeUndefined() 101 | 102 | // }) 103 | -------------------------------------------------------------------------------- /test/interpreter-pocket-list.test.js: -------------------------------------------------------------------------------- 1 | const interpreter = require('../src/cli/interpreter') 2 | const {commands} = require('../src/cli/menu') 3 | require('../src/pocket/pocket-commands') 4 | 5 | test('basic commands are loaded', async () => { 6 | expect(Object.entries(commands).length).toBeGreaterThan(1) 7 | }) 8 | 9 | test('list with search parameters', async () => { 10 | const action = interpreter('list bitcoin yesterday') 11 | expect(action.command).toBe(commands.list) 12 | expect(action.input).toEqual(['bitcoin', 'yesterday']) 13 | }) 14 | 15 | test('assume list as default command name', async () => { 16 | const action = interpreter('nodejs') 17 | expect(action.command).toBe(commands.list) 18 | expect(action.input).toEqual(['nodejs']) 19 | }) 20 | 21 | test('aliases and reserved words', async () => { 22 | const action = interpreter('search nodejs unread') 23 | expect(action.command).toBe(commands.list) 24 | expect(action.input).toEqual(['nodejs', 'unread']) 25 | }) 26 | 27 | test('parse', async () => { 28 | const action = interpreter('list') 29 | const query = action.parse() 30 | expect(query.name).toEqual('pocket-list') 31 | }) 32 | -------------------------------------------------------------------------------- /test/interpreter.test.js: -------------------------------------------------------------------------------- 1 | const interpreter = require('../src/cli/interpreter') 2 | require('../src/cli/help') 3 | require('../src/cli/quit') 4 | require('../src/cli/version') 5 | require('../src/cli/update') 6 | 7 | const commands = require('../src/cli/menu').commands 8 | 9 | test('undefined', () => { 10 | const nullAction = interpreter() 11 | expect(nullAction.command.name).toBe('null') 12 | }) 13 | 14 | test('quit', () => { 15 | const action = interpreter('quit') 16 | expect(action.command).toBe(commands.quit) 17 | const query = action.parse() 18 | expect(query.name).toBe('quit') 19 | }) 20 | 21 | test('help', () => { 22 | const action = interpreter('help') 23 | expect(action.command).toBe(commands.help) 24 | const query = action.parse() 25 | expect(query.name).toBe('help') 26 | }) 27 | 28 | test('version', () => { 29 | const action = interpreter('version') 30 | expect(action.command).toBe(commands.version) 31 | const query = action.parse() 32 | expect(query.name).toBe('version') 33 | }) 34 | 35 | test('update', () => { 36 | const action = interpreter('update') 37 | expect(action.command).toBe(commands.update) 38 | const query = action.parse() 39 | expect(query.name).toBe('update') 40 | }) 41 | 42 | test('you never know who is watching', () => { 43 | expect(null).toBeFalsy() 44 | }) 45 | -------------------------------------------------------------------------------- /test/intersections.test.js: -------------------------------------------------------------------------------- 1 | const {intersection, reverseIntersection} = require('../src/arrays-utils') 2 | 3 | test('use intersection', () => { 4 | const inputs = ['bitcoin', 'unread'] 5 | const reserved = ['unread', 'oldest'] 6 | const found = intersection([reserved, inputs]) 7 | expect(found).toEqual(['unread']) 8 | }) 9 | 10 | test('use native some', () => { 11 | const inputs = ['bitcoin', 'unread'] 12 | const reserved = ['unread', 'oldest'] 13 | const found = reserved.some(r => inputs.includes(r)) 14 | expect(found).toBe(true) 15 | }) 16 | 17 | test('reverse intersection', () => { 18 | const inputs = ['bitcoin', 'unread', 'development', 'oldest'] 19 | const reserved = ['unread', 'oldest', 'newest', 'favorite'] 20 | const found = reverseIntersection([inputs, reserved]) 21 | expect(found).toEqual(['bitcoin', 'development']) 22 | }) 23 | 24 | test('reverse intersection for tags', () => { 25 | const inputs = ['1', '2', 'tag1', '4', 'tag2'] 26 | const reserved = ['1', '2', '3', '4'] 27 | const found = reverseIntersection([inputs, reserved]) 28 | expect(found).toEqual(['tag1', 'tag2']) 29 | }) 30 | -------------------------------------------------------------------------------- /test/load-templates-from-file.test.js: -------------------------------------------------------------------------------- 1 | const templates = require('../src/templates') 2 | 3 | test('load help from file', () => { 4 | const template = 'Hello ${this.name}!' 5 | const vars = { 6 | name: 'world' 7 | } 8 | expect(templates(template, vars)).toBe('Hello world!') 9 | }) 10 | -------------------------------------------------------------------------------- /test/menu.test.js: -------------------------------------------------------------------------------- 1 | const menu = require('../src/cli/menu') 2 | 3 | test('same instance', () => { 4 | const menu1 = require('../src/cli/menu') 5 | const menu2 = require('../src/cli/menu') 6 | expect(menu1).toBe(menu2) 7 | }) 8 | 9 | test('change', () => { 10 | const menu1 = require('../src/cli/menu') 11 | const menu2 = require('../src/cli/menu') 12 | menu1.change({a: {a: 1}}) 13 | menu2.change({a: {a: 2}}) 14 | expect(menu1.get('a')).toEqual({a: 2}) 15 | expect(menu2.get('a')).toEqual({a: 2}) 16 | }) 17 | -------------------------------------------------------------------------------- /test/open.test.js: -------------------------------------------------------------------------------- 1 | const open = require('../src/cli/open') 2 | test('open per platform', () => { 3 | expect(open.get('linux')).toBe('xdg-open') 4 | expect(open.get('darwin')).toBe('open') 5 | // expect(open.get()).toBe('xdg-open') 6 | }) 7 | -------------------------------------------------------------------------------- /test/pocket-actions.test.js: -------------------------------------------------------------------------------- 1 | const {archive, favorite, tag, readd} = require('../src/pocket/pocket-parse') 2 | const localArticles = require('../src/local-articles') 3 | const mockArticles = require('./fixtures/mock.parsed.articles') 4 | localArticles.store(mockArticles) 5 | 6 | test('Archive', () => { 7 | const query = archive(['2']) 8 | expect(query.name).toEqual('pocket-modify') 9 | expect(query.actions).toHaveLength(1) 10 | // expect(query.actions).toBe('archive') 11 | expect(query.actions[0].action).toBe('archive') 12 | expect(query.actions[0].item_id).toBe('m2') 13 | }) 14 | 15 | test('Archive no index', () => { 16 | const query = archive([]) 17 | expect(query.name).toEqual('pocket-modify') 18 | expect(query.actions).toHaveLength(0) 19 | }) 20 | 21 | test('Archive with multiple indexes', () => { 22 | const query = archive(['1', '3', '4']) 23 | expect(query.name).toEqual('pocket-modify') 24 | expect(query.actions).toHaveLength(3) 25 | expect(query.actions[0].action).toBe('archive') 26 | expect(query.actions[0].item_id).toBe('m1') 27 | expect(query.actions[1].action).toBe('archive') 28 | expect(query.actions[1].item_id).toBe('m3') 29 | }) 30 | 31 | test('Favorite multiple items', () => { 32 | const query = favorite(['1', '2', '4']) 33 | expect(query.name).toEqual('pocket-modify') 34 | expect(query.actions).toHaveLength(3) 35 | expect(query.actions[0].action).toBe('favorite') 36 | expect(query.actions[0].item_id).toBe('m1') 37 | expect(query.actions[1].action).toBe('favorite') 38 | expect(query.actions[1].item_id).toBe('m2') 39 | }) 40 | 41 | test('Readd multiple items', () => { 42 | const query = readd(['1', '2', '4']) 43 | expect(query.name).toEqual('pocket-modify') 44 | expect(query.actions).toHaveLength(3) 45 | expect(query.actions[0].action).toBe('readd') 46 | expect(query.actions[0].item_id).toBe('m1') 47 | expect(query.actions[1].action).toBe('readd') 48 | expect(query.actions[1].item_id).toBe('m2') 49 | }) 50 | 51 | test('Tag - add', () => { 52 | const query = tag(['1', '2', 'aTag', '4', 'anotherTag']) 53 | expect(query.name).toEqual('pocket-modify') 54 | expect(query.actions).toHaveLength(3) 55 | expect(query.actions[0].action).toBe('tags_add') 56 | expect(query.actions[0].item_id).toBe('m1') 57 | expect(query.actions[0].tags).toEqual('aTag, anotherTag') 58 | }) 59 | -------------------------------------------------------------------------------- /test/pocket-articles.test.js: -------------------------------------------------------------------------------- 1 | const local1 = require('../src/local-articles') 2 | const local2 = require('../src/local-articles') 3 | 4 | test('ensure they are the same', () => { 5 | expect(local1).toBe(local2) 6 | }) 7 | 8 | test('one more small test makes no harm..', () => { 9 | const item2 = {a: 2} 10 | local1.store([{a: 1}, item2]) 11 | expect(local2.get(2)).toEqual(item2) 12 | expect(local2.get(2)).not.toBe(item2) 13 | }) 14 | -------------------------------------------------------------------------------- /test/pocket-auth-learning-test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const expect = require('chai').expect 3 | 4 | require('dotenv').config() 5 | const axios = require('axios') 6 | const redirectURI = 'http://localhost:3344/oauth/pocket/callback' 7 | 8 | test('pochet oauth', async () => { 9 | const client = axios.create({ 10 | baseURL: 'https://getpocket.com/v3', 11 | }) 12 | const response = await client.post('/oauth/request', { 13 | consumer_key: process.env.POCKET, 14 | redirect_uri: `${redirectURI}`, 15 | state: 'ok' 16 | }) 17 | expect(response.data).toBeDefined() 18 | expect(response.data).toContain('code=') 19 | // expect(response.data).to.be.a('string') 20 | // expect(response.data).to.contain('code=') 21 | // expect(response.data).to.contain('state=') 22 | const code = response.data.split('&')[0].split('=')[1] 23 | const state = response.data.split('&')[1].split('=')[1] 24 | // console.log(code, 'the request token to be associated to user session') 25 | assert.strict.equal(state, 'ok') 26 | // expect(state).to.be('ok') 27 | // console.log(`https://getpocket.com/auth/authorize?request_token=${code}&redirect_uri=${redirectURI}`) 28 | // expect(response.data).to.have.property('b').but.not.own.property('b'); 29 | }) 30 | 31 | test('pochet oauth json', async () => { 32 | const client = axios.create({ 33 | baseURL: 'https://getpocket.com/v3', 34 | headers: { 35 | 'Content-Type': 'application/json; charset=UTF-8', 36 | 'X-Accept': 'application/json' 37 | } 38 | }) 39 | 40 | const response = await client.post('/oauth/request', { 41 | consumer_key: process.env.POCKET, 42 | redirect_uri: `${redirectURI}`, 43 | state: 'ok' 44 | }) 45 | 46 | // expect(response.data.code).to.be.a('string') 47 | // expect(response.data.state).to.be.a('string') 48 | const code = response.data.code 49 | const state = response.data.state 50 | console.log(code, 'the request token to be associated to user session') 51 | assert.strict.equal(state, 'ok') 52 | console.log(`https://getpocket.com/auth/authorize?request_token=${code}&redirect_uri=${redirectURI}`) 53 | }) 54 | -------------------------------------------------------------------------------- /test/pocket-list.test.js: -------------------------------------------------------------------------------- 1 | const pocket = require('../src/pocket/pocket-parse') 2 | 3 | const {list} = require('../src/pocket/pocket-parse') 4 | 5 | pocket.client = { 6 | read: () => [], 7 | modify: () => [], 8 | add: () => [] 9 | } 10 | 11 | test('retrieve defaults', () => { 12 | const output = list() 13 | expect(output.name).toBe('pocket-list') 14 | expect(output.search.count).toEqual(8) 15 | expect(output.search.detailType).toEqual('complete') 16 | expect(output.search.search).toEqual('') 17 | // expect(output.search.since).toEqual('') 18 | expect(output.search.sort).toEqual('newest') 19 | expect(output.search.state).toEqual('all') 20 | }) 21 | 22 | test('search params', () => { 23 | const output = list(['bitcoin', 'block']) 24 | expect(output.search.search).toEqual('bitcoin block') 25 | }) 26 | 27 | test('reserverd - unread', () => { 28 | const output = list(['unread', 'nodejs']) 29 | expect(output.search.state).toEqual('unread') 30 | expect(output.search.search).toEqual('nodejs') 31 | }) 32 | 33 | test('json query to human readable text', () => { 34 | list(['unread', 'nodejs', 'oldest']) 35 | // expect(pocket.toHumanText()).toContain('nodejs') 36 | // expect(pocket.toHumanText()).toContain('unread') 37 | // expect(pocket.toHumanText()).toContain('oldest') 38 | // // expect(pocket.toHumanText()).toBe('Search for nodejs in unread documents, order by oldest starting January 1, 1970') 39 | // await list(['bitcoin', 'site']) 40 | // expect(pocket.toHumanText()).toContain('bitcoin') 41 | // expect(pocket.toHumanText()).toContain('site') 42 | // expect(pocket.toHumanText()).toContain('all') 43 | // console.log(pocket.toHumanText()) 44 | }) 45 | -------------------------------------------------------------------------------- /test/test-helpers.js: -------------------------------------------------------------------------------- 1 | jest.setTimeout(500) 2 | expect.extend({ 3 | toHaveStatus (response, expectedStatus) { 4 | return { 5 | pass: response.status === expectedStatus, 6 | message: () => `expected ${expectedStatus} got ${response.status} with body: ${JSON.stringify(response.body)}` 7 | } 8 | } 9 | }) 10 | --------------------------------------------------------------------------------