├── LICENSE.md ├── README.md ├── archivist-cli ├── .gitignore ├── cli.js ├── lib.js ├── package-lock.json └── package.json ├── archivist-pinboard ├── .gitignore ├── fetch │ ├── assets │ │ └── .gitkeep │ ├── fetcher.js │ └── index.js ├── index.js ├── package-lock.json ├── package.json ├── query │ └── index.js └── scripts │ └── compile-freeze-dry.js ├── archivist-pinterest-crawl ├── .gitignore ├── fetch │ ├── crawler.js │ ├── fetcher.js │ └── index.js ├── index.js ├── package-lock.json ├── package.json └── query │ └── index.js ├── archivist-ui ├── .gitignore ├── README.md ├── assets │ └── Archivist.icns ├── package-lock.json ├── package.json ├── scripts │ └── install.sh └── src │ ├── main │ └── index.js │ └── renderer │ ├── index.html │ └── index.js └── assets └── screenshot.png /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szymon Kaliski 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 | # Archivist 2 | 3 | Tool for archiving and exploring. 4 | 5 | Built out of a need to get out of walled gardens of Pinterest and (much less walled) Pinboard. 6 | 7 | **Alpha quality at best.** 8 | 9 |

10 | 11 | ## Modules 12 | 13 | Archivist is built out of three interconnected parts (each package has it's own readme file): 14 | 15 | 1. [`archivist-cli`](./archivist-cli) - command line tool for configuration, fetching and querying the data 16 | 2. [`archivist-ui`](./archivist-ui) - Electron UI built on top of `archivist-cli` 17 | 3. `archivist-*` - various crawlers, "official" ones: 18 | - [`archivist-pinboard`](./archivist-pinboard) - API-based Pinboard archiving: screenshot and [freeze-dry](https://github.com/WebMemex/freeze-dry) of the original website 19 | - [`archivist-pinterest-crawl`](./archivist-pinterest-crawl) - slowly crawl through Pinterest and archive pin image 20 | 21 | ## Installation 22 | 23 | 1. `npm install -g archivist-cli` 24 | 2. to archive pinboard: `npm install -g archivist-pinboard` 25 | 3. to archive pinterest: `npm install -g archivist-pinterest-crawl` 26 | 27 | `archivist-ui` is not on npm (it should probably be a downloadable `dmg`, but I didn't get around to it), so to generate the `.app` and put it in `/Applications/` yourself: 28 | 29 | 1. clone this repo 30 | 2. `cd archivist-ui && ./scripts/install.sh` 31 | 32 | ## Configure 33 | 34 | ```bash 35 | $ archivist config 36 | ``` 37 | 38 | Config is a JSON object of shape: 39 | 40 | ```json 41 | { 42 | "crawler-1": CRAWLER_1_OPTIONS, 43 | "crawler-2": CRAWLER_2_OPTIONS, 44 | ... 45 | } 46 | ``` 47 | 48 | Example config (assuming Pinboard and Pinterest backup): 49 | 50 | ```json 51 | { 52 | "archivist-pinterest-crawl": { 53 | "loginMethod": "cookies", 54 | "profile": "szymon_k" 55 | }, 56 | "archivist-pinboard": { 57 | "apiKey": "API_KEY_FOR_PINBOARD" 58 | } 59 | } 60 | ``` 61 | 62 | `archivist-pinterest-crawl` supports two login methods: `"cookies"` (which uses cookies from local Google Chrome installation) or `"password"` which requires plaintext username and password: 63 | 64 | ```json 65 | "archivist-pinterest-crawl": { 66 | "loginMethod": "password", 67 | "username": "PINTEREST_USERNAME", 68 | "password": "PINTEREST_PASSWORD", 69 | "profile": "szymon_k" 70 | }, 71 | ``` 72 | 73 | `archivist-pinboard` requires `API Token` from https://pinboard.in/settings/password to run properly. 74 | 75 | ## Usage 76 | 77 | - backup data: `archivist fetch` (might take a long time depending on the size of the archive) 78 | - list everything: `archivist query` 79 | - find everything about keyboards: `archivist query keyboard` 80 | - `query` by default returns `ndjson`, normal JSON can be outputed using `--json` 81 | 82 | ## References 83 | 84 | - [kollektor](https://github.com/vorg/kollektor) - no-ui self-hosted Pinterest clone 85 | - gwern on [archiving URLs](https://www.gwern.net/Archiving-URLs) 86 | - [freeze-dry implementation notes](https://github.com/WebMemex/freeze-dry/blob/master/src/Readme.md) 87 | 88 | -------------------------------------------------------------------------------- /archivist-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /archivist-cli/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs"); 4 | const yargs = require("yargs"); 5 | const { spawn } = require("child_process"); 6 | 7 | const { fetch, search } = require("./lib"); 8 | 9 | const args = yargs 10 | .command("config", "open configuration file") 11 | .command("fetch", "fetch all configured crawlers") 12 | .command("search", "search all crawlers", (yargs) => { 13 | yargs.option("json", { description: "output as JSON" }); 14 | }) 15 | .demandCommand(1, "you need to provide a command") 16 | .help().argv; 17 | 18 | const [TYPE] = args._; 19 | 20 | if (TYPE === "config") { 21 | const editor = process.env.EDITOR || "vim"; 22 | 23 | if (!fs.existsSync(CONFIG_FILE)) { 24 | fs.writeFileSync( 25 | JSON.stringify(DEFAULT_CONFIG, null, 2), 26 | CONFIG_FILE, 27 | "utf-8" 28 | ); 29 | } 30 | 31 | spawn(editor, [CONFIG_FILE], { stdio: "inherit" }); 32 | } else if (TYPE === "fetch") { 33 | fetch(); 34 | } else if (TYPE === "search") { 35 | search(args._[1]).then((result) => { 36 | if (args.json) { 37 | console.log(JSON.stringify(result.value(), null, 2)); 38 | } else { 39 | result.forEach((d) => console.log(JSON.stringify(d))).value(); 40 | } 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /archivist-cli/lib.js: -------------------------------------------------------------------------------- 1 | const async = require("async"); 2 | const envPaths = require("env-paths"); 3 | const mkdirp = require("mkdirp"); 4 | const os = require("os"); 5 | const path = require("path"); 6 | const { chain } = require("lodash"); 7 | 8 | const CONFIG_PATH = envPaths("archivist").config; 9 | const CONFIG_FILE = path.join(CONFIG_PATH, "config.json"); 10 | const DEFAULT_CONFIG = {}; 11 | 12 | mkdirp(CONFIG_PATH); 13 | 14 | const loadConfig = () => { 15 | let config; 16 | 17 | try { 18 | config = require(CONFIG_FILE); 19 | } catch (e) { 20 | console.log(e); 21 | process.exit(1); 22 | } 23 | 24 | return config; 25 | }; 26 | 27 | const loadCrawler = (name) => { 28 | return new Promise((resolve, reject) => { 29 | let crawler; 30 | 31 | try { 32 | crawler = require(name); 33 | } catch (e) { 34 | return reject(e); 35 | } 36 | 37 | resolve(crawler); 38 | }); 39 | }; 40 | 41 | const fetch = () => { 42 | return new Promise((resolve, reject) => { 43 | async.eachLimit( 44 | Object.entries(loadConfig()), 45 | os.cpus().length, 46 | ([name, config], callback) => { 47 | loadCrawler(name).then((crawler) => 48 | crawler(config) 49 | .fetch(config) 50 | .then(callback) 51 | .catch((e) => callback(`[${name}] fetching error ${e}`)) 52 | ); 53 | }, 54 | (err) => { 55 | if (err) { 56 | return reject(err); 57 | } 58 | 59 | resolve(); 60 | } 61 | ); 62 | }); 63 | }; 64 | 65 | const search = (query) => { 66 | return new Promise((resolve, reject) => { 67 | async.mapLimit( 68 | Object.entries(loadConfig()), 69 | os.cpus().length, 70 | ([name, config], callback) => { 71 | loadCrawler(name).then((crawler) => { 72 | crawler(config) 73 | .query(query) 74 | .then((result) => callback(null, result)) 75 | .catch((e) => callback(`[${name}] search error ${e}`)); 76 | }); 77 | }, 78 | (err, result) => { 79 | if (err) { 80 | return reject(err); 81 | } 82 | 83 | const sortedResult = chain(result) 84 | .flatten() 85 | .sortBy((d) => new Date(d.time)); 86 | 87 | resolve(sortedResult); 88 | } 89 | ); 90 | }); 91 | }; 92 | 93 | module.exports = { loadConfig, loadCrawler, fetch, search }; 94 | -------------------------------------------------------------------------------- /archivist-cli/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-cli", 3 | "version": "1.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/color-name": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 10 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" 11 | }, 12 | "ansi-regex": { 13 | "version": "5.0.0", 14 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 15 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 16 | }, 17 | "ansi-styles": { 18 | "version": "4.2.1", 19 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 20 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 21 | "requires": { 22 | "@types/color-name": "^1.1.1", 23 | "color-convert": "^2.0.1" 24 | } 25 | }, 26 | "async": { 27 | "version": "3.2.0", 28 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", 29 | "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" 30 | }, 31 | "camelcase": { 32 | "version": "5.3.1", 33 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 34 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 35 | }, 36 | "cliui": { 37 | "version": "6.0.0", 38 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", 39 | "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", 40 | "requires": { 41 | "string-width": "^4.2.0", 42 | "strip-ansi": "^6.0.0", 43 | "wrap-ansi": "^6.2.0" 44 | } 45 | }, 46 | "color-convert": { 47 | "version": "2.0.1", 48 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 49 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 50 | "requires": { 51 | "color-name": "~1.1.4" 52 | } 53 | }, 54 | "color-name": { 55 | "version": "1.1.4", 56 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 57 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 58 | }, 59 | "decamelize": { 60 | "version": "1.2.0", 61 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 62 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 63 | }, 64 | "emoji-regex": { 65 | "version": "8.0.0", 66 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 67 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 68 | }, 69 | "env-paths": { 70 | "version": "2.2.0", 71 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", 72 | "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" 73 | }, 74 | "find-up": { 75 | "version": "4.1.0", 76 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 77 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 78 | "requires": { 79 | "locate-path": "^5.0.0", 80 | "path-exists": "^4.0.0" 81 | } 82 | }, 83 | "get-caller-file": { 84 | "version": "2.0.5", 85 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 86 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 87 | }, 88 | "is-fullwidth-code-point": { 89 | "version": "3.0.0", 90 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 91 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 92 | }, 93 | "locate-path": { 94 | "version": "5.0.0", 95 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 96 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 97 | "requires": { 98 | "p-locate": "^4.1.0" 99 | } 100 | }, 101 | "lodash": { 102 | "version": "4.17.15", 103 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 104 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 105 | }, 106 | "mkdirp": { 107 | "version": "1.0.4", 108 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 109 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" 110 | }, 111 | "p-limit": { 112 | "version": "2.3.0", 113 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 114 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 115 | "requires": { 116 | "p-try": "^2.0.0" 117 | } 118 | }, 119 | "p-locate": { 120 | "version": "4.1.0", 121 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 122 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 123 | "requires": { 124 | "p-limit": "^2.2.0" 125 | } 126 | }, 127 | "p-try": { 128 | "version": "2.2.0", 129 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 130 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 131 | }, 132 | "path-exists": { 133 | "version": "4.0.0", 134 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 135 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 136 | }, 137 | "require-directory": { 138 | "version": "2.1.1", 139 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 140 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 141 | }, 142 | "require-main-filename": { 143 | "version": "2.0.0", 144 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 145 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 146 | }, 147 | "set-blocking": { 148 | "version": "2.0.0", 149 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 150 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 151 | }, 152 | "string-width": { 153 | "version": "4.2.0", 154 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 155 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 156 | "requires": { 157 | "emoji-regex": "^8.0.0", 158 | "is-fullwidth-code-point": "^3.0.0", 159 | "strip-ansi": "^6.0.0" 160 | } 161 | }, 162 | "strip-ansi": { 163 | "version": "6.0.0", 164 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 165 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 166 | "requires": { 167 | "ansi-regex": "^5.0.0" 168 | } 169 | }, 170 | "which-module": { 171 | "version": "2.0.0", 172 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 173 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 174 | }, 175 | "wrap-ansi": { 176 | "version": "6.2.0", 177 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 178 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 179 | "requires": { 180 | "ansi-styles": "^4.0.0", 181 | "string-width": "^4.1.0", 182 | "strip-ansi": "^6.0.0" 183 | } 184 | }, 185 | "y18n": { 186 | "version": "4.0.0", 187 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 188 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 189 | }, 190 | "yargs": { 191 | "version": "15.3.1", 192 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", 193 | "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", 194 | "requires": { 195 | "cliui": "^6.0.0", 196 | "decamelize": "^1.2.0", 197 | "find-up": "^4.1.0", 198 | "get-caller-file": "^2.0.1", 199 | "require-directory": "^2.1.1", 200 | "require-main-filename": "^2.0.0", 201 | "set-blocking": "^2.0.0", 202 | "string-width": "^4.2.0", 203 | "which-module": "^2.0.0", 204 | "y18n": "^4.0.0", 205 | "yargs-parser": "^18.1.1" 206 | } 207 | }, 208 | "yargs-parser": { 209 | "version": "18.1.3", 210 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", 211 | "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", 212 | "requires": { 213 | "camelcase": "^5.0.0", 214 | "decamelize": "^1.2.0" 215 | } 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /archivist-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-cli", 3 | "version": "1.2.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 7 | "license": "MIT", 8 | "bin": { 9 | "archivist": "./cli.js" 10 | }, 11 | "dependencies": { 12 | "async": "^3.2.0", 13 | "env-paths": "^2.2.0", 14 | "lodash": "^4.17.15", 15 | "mkdirp": "^1.0.4", 16 | "yargs": "^15.3.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /archivist-pinboard/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | fetch/assets/freeze-dry-browserified.js 3 | .DS_Store 4 | .env 5 | -------------------------------------------------------------------------------- /archivist-pinboard/fetch/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/archivist/2061bc231b03437313f9f2eb41fdaec66aeebbe9/archivist-pinboard/fetch/assets/.gitkeep -------------------------------------------------------------------------------- /archivist-pinboard/fetch/fetcher.js: -------------------------------------------------------------------------------- 1 | const async = require("async"); 2 | const envPaths = require("env-paths"); 3 | const fs = require("fs"); 4 | const isReachable = require("is-reachable"); 5 | const md5 = require("md5"); 6 | const mkdirp = require("mkdirp"); 7 | const path = require("path"); 8 | const puppeteer = require("puppeteer"); 9 | const wayback = require("wayback-machine"); 10 | 11 | const DATA_PATH = envPaths("archivist-pinboard").data; 12 | const ASSETS_PATH = path.join(DATA_PATH, "assets"); 13 | const FROZEN_PATH = path.join(DATA_PATH, "frozen"); 14 | 15 | mkdirp(ASSETS_PATH); 16 | mkdirp(FROZEN_PATH); 17 | 18 | const FREEZE_DRY_PATH = path.join( 19 | __dirname, 20 | "./assets/freeze-dry-browserified.js" 21 | ); 22 | 23 | const FREEZE_DRY_SRC = fs.readFileSync(FREEZE_DRY_PATH, "utf-8"); 24 | 25 | const savePageInternal = async (browser, link, url) => { 26 | const screenshotPath = path.join(ASSETS_PATH, `${md5(url)}.png`); 27 | const frozenPath = path.join(FROZEN_PATH, `${md5(url)}.html`); 28 | 29 | let didScreenshot, didFreeze, didOpen; 30 | 31 | // don't re-download stuff 32 | if (fs.existsSync(screenshotPath) && fs.existsSync(frozenPath)) { 33 | console.log("[archivist-pinboard]", "already downloaded:", link); 34 | return { 35 | screenshot: path.basename(screenshotPath), 36 | frozen: path.basename(frozenPath) 37 | }; 38 | } 39 | 40 | console.log("[archivist-pinboard]", "saving:", link); 41 | 42 | const page = await browser.newPage(); 43 | 44 | page.on("error", async () => { 45 | await page.close(); 46 | 47 | return null; 48 | }); 49 | 50 | await page.setViewport({ width: 1920, height: 1080, deviceScaleRatio: 2 }); 51 | 52 | try { 53 | // await page.goto(link, { waitUntil: "networkidle2" }); 54 | await page.goto(link, { waitUntil: "load" }); 55 | 56 | didOpen = true; 57 | } catch (e) { 58 | console.log( 59 | "[archivist-pinboard]", 60 | "error navigating:", 61 | link, 62 | e.toString() 63 | ); 64 | 65 | didOpen = false; 66 | } 67 | 68 | if (!didOpen) { 69 | await page.close(); 70 | return null; 71 | } 72 | 73 | try { 74 | console.log("[archivist-pinboard]", "screenshot:", link); 75 | await page.screenshot({ path: screenshotPath }); 76 | 77 | didScreenshot = true; 78 | } catch (e) { 79 | didScreenshot = false; 80 | } 81 | 82 | try { 83 | console.log("[archivist-pinboard]", "freeze:", link); 84 | await page.evaluate(FREEZE_DRY_SRC); 85 | 86 | const frozen = await Promise.race([ 87 | page.evaluate(async () => await window.freezeDry()), 88 | page.waitFor(5000) 89 | ]); 90 | 91 | fs.writeFileSync(frozenPath, frozen, "utf-8"); 92 | 93 | didFreeze = true; 94 | } catch (e) { 95 | didFreeze = false; 96 | } 97 | 98 | await page.close(); 99 | 100 | return { 101 | screenshot: 102 | didScreenshot === true ? path.basename(screenshotPath) : undefined, 103 | frozen: didFreeze === true ? path.basename(frozenPath) : undefined 104 | }; 105 | }; 106 | 107 | const savePage = async (browser, link) => { 108 | const isOnline = await isReachable(link); 109 | 110 | if (!isOnline) { 111 | console.log("[archivist-pinboard]", "offline, trying wayback for:", link); 112 | 113 | return new Promise(resolve => { 114 | wayback.getClosest(link, (err, closest) => { 115 | const isError = !!err; 116 | const isClosest = closest && !!closest.available && !!closest.url; 117 | 118 | if (isError || !isClosest) { 119 | console.log( 120 | "[archivist-pinboard]", 121 | "couldn't find wayback for:", 122 | link 123 | ); 124 | resolve(null); 125 | } else { 126 | console.log( 127 | "[archivist-pinboard]", 128 | "found wayback for", 129 | link, 130 | "->", 131 | closest.url 132 | ); 133 | 134 | savePageInternal(browser, closest.url, link).then(paths => 135 | resolve(paths) 136 | ); 137 | } 138 | }); 139 | }); 140 | } 141 | 142 | return await savePageInternal(browser, link, link); 143 | }; 144 | 145 | const run = async links => { 146 | const headless = true; 147 | const browser = await puppeteer.launch({ headless, ignoreHTTPSErrors: true }); 148 | 149 | return new Promise((resolve, reject) => { 150 | async.mapLimit( 151 | links, 152 | 10, 153 | (link, callback) => { 154 | savePage(browser, link.href) 155 | .then(paths => { 156 | callback(null, { ...link, paths }); 157 | }) 158 | .catch(e => { 159 | console.log( 160 | "[archivist-pinboard]", 161 | "uncatched error", 162 | link.href, 163 | e.toString() 164 | ); 165 | // ignoring errors for now 166 | callback(null, null); 167 | }); 168 | }, 169 | (err, res) => { 170 | browser.close().then(() => { 171 | if (err) { 172 | reject(err); 173 | } else { 174 | resolve(res); 175 | } 176 | }); 177 | } 178 | ); 179 | }); 180 | }; 181 | 182 | module.exports = run; 183 | -------------------------------------------------------------------------------- /archivist-pinboard/fetch/index.js: -------------------------------------------------------------------------------- 1 | const async = require("async"); 2 | const Database = require("better-sqlite3"); 3 | const Pinboard = require("node-pinboard"); 4 | const envPaths = require("env-paths"); 5 | const fs = require("fs"); 6 | const mkdirp = require("mkdirp"); 7 | const path = require("path"); 8 | const { isString } = require("lodash"); 9 | 10 | const fetcher = require("./fetcher"); 11 | 12 | const DATA_PATH = envPaths("archivist-pinboard").data; 13 | const ASSETS_PATH = path.join(DATA_PATH, "assets"); 14 | const FROZEN_PATH = path.join(DATA_PATH, "frozen"); 15 | 16 | mkdirp(ASSETS_PATH); 17 | mkdirp(FROZEN_PATH); 18 | 19 | const CRAWLED_DATA_PATH = path.join(DATA_PATH, "crawled-links.json"); 20 | 21 | const processRemovedLinks = async (removedLinks) => { 22 | return new Promise((resolve) => { 23 | async.mapLimit( 24 | removedLinks, 25 | 10, 26 | (item, callback) => { 27 | const screenshotPath = 28 | item.screenshot && path.join(ASSETS_PATH, item.screenshot); 29 | 30 | const frozenPath = item.frozen && path.join(FROZEN_PATH, item.frozen); 31 | 32 | if (screenshotPath && fs.existsSync(screenshotPath)) { 33 | console.log("[archivist-pinboard]", `unlinking ${screenshotPath}`); 34 | fs.unlinkSync(screenshotPath); 35 | } 36 | 37 | if (frozenPath && fs.existsSync(frozenPath)) { 38 | console.log("[archivist-pinboard]", `unlinking ${frozenPath}`); 39 | fs.unlinkSync(frozenPath); 40 | } 41 | 42 | callback(null, item.hash); 43 | }, 44 | (err, hashes) => resolve(hashes) 45 | ); 46 | }); 47 | }; 48 | 49 | const run = async (options) => { 50 | if (!options.apiKey) { 51 | throw new Error("apiKey not provided"); 52 | } 53 | 54 | const pinboard = new Pinboard(options.apiKey); 55 | 56 | const crawlLinks = async () => 57 | new Promise((resolve, reject) => { 58 | pinboard.all((err, links) => { 59 | if (err) { 60 | reject(err); 61 | } else { 62 | resolve(links); 63 | } 64 | }); 65 | }); 66 | 67 | const db = new Database(path.join(DATA_PATH, "data.db")); 68 | 69 | db.prepare( 70 | ` 71 | CREATE TABLE IF NOT EXISTS data ( 72 | href TEXT, 73 | hash TEXT PRIMARY KEY, 74 | meta TEXT, 75 | description TEXT, 76 | extended TEXT, 77 | tags TEXT, 78 | time DATETIME, 79 | screenshot TEXT, 80 | frozen TEXT 81 | ) 82 | ` 83 | ).run(); 84 | 85 | const search = db.prepare( 86 | "SELECT count(hash) AS count FROM data WHERE hash = ?" 87 | ); 88 | 89 | const insert = db.prepare( 90 | `INSERT OR REPLACE INTO data (href, hash, meta, description, extended, tags, time, screenshot, frozen) 91 | VALUES (:href, :hash, :meta, :description, :extended, :tags, :time, :screenshot, :frozen)` 92 | ); 93 | 94 | const remove = db.prepare("DELETE FROM data WHERE hash = ?"); 95 | 96 | const dbLinks = db.prepare("SELECT * FROM data").all(); 97 | 98 | let crawledLinks = await crawlLinks(); 99 | 100 | // not sure what's going on in here really 101 | if (isString(crawledLinks)) { 102 | try { 103 | crawledLinks = JSON.parse(crawledLinks.slice(1)); 104 | } catch (e) {} 105 | } 106 | 107 | if (isString(crawledLinks)) { 108 | console.log("[archivist-pinboard] unrecoverable issue with crawled links"); 109 | return; 110 | } 111 | 112 | // const crawledLinks = require(CRAWLED_DATA_PATH); 113 | 114 | fs.writeFileSync( 115 | CRAWLED_DATA_PATH, 116 | JSON.stringify(crawledLinks, null, 2), 117 | "utf-8" 118 | ); 119 | // console.log("[archivist-pinboard]", `crawled data saved to ${CRAWLED_DATA_PATH}`); 120 | 121 | const newLinks = crawledLinks.filter( 122 | (link) => search.get(link.hash).count === 0 123 | ); 124 | 125 | const removedLinks = dbLinks.filter( 126 | ({ hash }) => !crawledLinks.find((l) => l.hash === hash) 127 | ); 128 | 129 | console.log( 130 | "[archivist-pinboard]", 131 | `all links: ${crawledLinks.length} / new links: ${newLinks.length} / removed links: ${removedLinks.length}` 132 | ); 133 | 134 | const hashesToRemove = await processRemovedLinks(removedLinks); 135 | 136 | const removeLinks = db.transaction((hashes) => { 137 | hashes.forEach((hash) => remove.run(hash)); 138 | }); 139 | 140 | removeLinks(hashesToRemove); 141 | 142 | const fetchedLinks = await fetcher(newLinks); 143 | 144 | const finalLinks = fetchedLinks 145 | .filter((link) => link && link.paths) 146 | .map((link) => ({ 147 | href: link.href, 148 | hash: link.hash, 149 | meta: link.meta, 150 | description: link.description, 151 | extended: link.extended, 152 | tags: link.tags, 153 | time: link.time, 154 | screenshot: link.paths.screenshot, 155 | frozen: link.paths.frozen, 156 | })); 157 | 158 | const insertLinks = db.transaction((links) => { 159 | links.forEach((link) => insert.run(link)); 160 | }); 161 | 162 | insertLinks(finalLinks); 163 | 164 | console.log( 165 | "[archivist-pinboard]", 166 | `insterted links: ${finalLinks.length} (of ${newLinks.length} new links)` 167 | ); 168 | }; 169 | 170 | module.exports = run; 171 | -------------------------------------------------------------------------------- /archivist-pinboard/index.js: -------------------------------------------------------------------------------- 1 | const fetch = require("./fetch"); 2 | const query = require("./query"); 3 | 4 | module.exports = options => ({ 5 | fetch: () => fetch(options), 6 | get: () => query(options), 7 | query: text => query(options, text) 8 | }); 9 | -------------------------------------------------------------------------------- /archivist-pinboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-pinboard", 3 | "version": "1.2.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "postinstall": "node scripts/compile-freeze-dry.js" 8 | }, 9 | "keywords": [], 10 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 11 | "license": "MIT", 12 | "dependencies": { 13 | "async": "^3.2.0", 14 | "better-sqlite3": "^6.0.1", 15 | "env-paths": "^2.2.0", 16 | "freeze-dry": "^0.2.4", 17 | "is-reachable": "^4.0.0", 18 | "lodash": "^4.17.15", 19 | "md5": "^2.2.1", 20 | "mkdirp": "^1.0.4", 21 | "node-pinboard": "^1.0.0", 22 | "puppeteer": "^3.0.1", 23 | "wayback-machine": "^0.2.1" 24 | }, 25 | "devDependencies": { 26 | "browserify": "^16.5.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /archivist-pinboard/query/index.js: -------------------------------------------------------------------------------- 1 | const envPaths = require("env-paths"); 2 | const Database = require("better-sqlite3"); 3 | const path = require("path"); 4 | 5 | const DATA_PATH = envPaths("archivist-pinboard").data; 6 | const ASSETS_PATH = path.join(DATA_PATH, "assets"); 7 | const FROZEN_PATH = path.join(DATA_PATH, "frozen"); 8 | 9 | const query = async (_, text) => { 10 | const db = new Database(path.join(DATA_PATH, "data.db")); 11 | let search; 12 | 13 | if (text) { 14 | search = db 15 | .prepare( 16 | ` 17 | SELECT * 18 | FROM data 19 | WHERE 20 | description LIKE :query OR 21 | extended LIKE :query OR 22 | tags LIKE :query OR 23 | href LIKE :query 24 | ` 25 | ) 26 | .all({ query: `%${text}%` }); 27 | } else { 28 | search = db.prepare("SELECT * FROM data").all(); 29 | } 30 | 31 | return search.map(d => ({ 32 | img: path.join(ASSETS_PATH, d.screenshot), 33 | link: d.href, 34 | id: d.hash, 35 | time: d.time, 36 | 37 | width: 1920, 38 | height: 1080, 39 | 40 | meta: { 41 | source: "pinboard", 42 | title: d.description, 43 | note: d.extended, 44 | tags: d.tags.split(" "), 45 | static: d.frozen ? path.join(FROZEN_PATH, d.frozen) : undefined 46 | } 47 | })); 48 | }; 49 | 50 | module.exports = query; 51 | -------------------------------------------------------------------------------- /archivist-pinboard/scripts/compile-freeze-dry.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const browserify = require("browserify"); 3 | 4 | const freezeDryFunction = require("fs").createWriteStream( 5 | path.resolve(__dirname, "../fetch/assets/freeze-dry-browserified.js") 6 | ); 7 | 8 | freezeDryFunction.write("window.freezeDry = (async () => {\n"); 9 | 10 | browserify() 11 | .require(path.resolve(__dirname, "../node_modules/freeze-dry/lib/index.js"), { 12 | expose: "freeze-dry" 13 | }) 14 | .bundle() 15 | .on("end", () => { 16 | freezeDryFunction.write('return await require("freeze-dry").default()})'); 17 | freezeDryFunction.end(); 18 | }) 19 | .pipe(freezeDryFunction, { end: false }); 20 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .creds.json 3 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/fetch/crawler.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const async = require("async"); 3 | const chrome = require("chrome-cookies-secure"); 4 | const puppeteer = require("puppeteer"); 5 | const { chain, flatten } = require("lodash"); 6 | 7 | const ROOT = "https://pinterest.com"; 8 | 9 | const sleep = time => new Promise(resolve => setTimeout(resolve, time)); 10 | 11 | const crawlPin = async (browser, pinUrl) => { 12 | console.log("[archivist-pinterest-crawl]", "crawling pin", pinUrl); 13 | 14 | const page = await browser.newPage(); 15 | await page.setViewport({ width: 1600, height: 900, deviceScaleRatio: 2 }); 16 | 17 | await page.goto(pinUrl, { waitUntil: "networkidle2" }); 18 | 19 | const { link, title, date } = await page.evaluate(() => { 20 | const getLink = () => { 21 | const link = document.querySelector(".linkModuleActionButton"); 22 | return link ? link.href : undefined; 23 | }; 24 | 25 | const getTitle = () => { 26 | const titleCard = document.querySelector(".CloseupTitleCard"); 27 | return titleCard ? titleCard.textContent : undefined; 28 | }; 29 | 30 | const getDate = () => { 31 | let date = undefined; 32 | 33 | try { 34 | date = Object.values( 35 | JSON.parse(document.getElementById("initial-state").innerText).pins 36 | ).map(p => p.created_at)[0]; 37 | } catch (e) {} 38 | 39 | return date; 40 | }; 41 | 42 | const getData = () => ({ 43 | link: getLink(), 44 | title: getTitle(), 45 | date: getDate() 46 | }); 47 | 48 | return new Promise(resolve => { 49 | const data = getData(); 50 | 51 | if (data.link || data.title || data.date) { 52 | resolve(data); 53 | } else { 54 | // try once more and give up 55 | setTimeout(() => { 56 | resolve(getData()); 57 | }, 1000); 58 | } 59 | }); 60 | }); 61 | 62 | await page.close(); 63 | 64 | return { link, title, date }; 65 | }; 66 | 67 | const crawlBoard = async (page, boardUrl) => { 68 | console.log("[archivist-pinterest-crawl]", "crawling board", boardUrl); 69 | 70 | await page.goto(boardUrl, { waitUntil: "networkidle2" }); 71 | 72 | await sleep(2000); // boards load slowly, but "networkidle0" causes timeout 73 | 74 | // scroll down to bottom (hopefully) 75 | const scrollResult = await page.evaluate( 76 | () => 77 | new Promise(resolve => { 78 | let lastScrollPosition = 0; 79 | const allPins = {}; 80 | 81 | const scrollDown = () => { 82 | window.scrollTo(0, window.scrollY + 10); 83 | 84 | setTimeout(() => { 85 | Array.from(document.querySelectorAll("[data-test-id=pin]")).forEach( 86 | pin => { 87 | const a = pin.querySelector("a"); 88 | const img = pin.querySelector("img"); 89 | 90 | if (a && img) { 91 | const url = a.href; 92 | const src = img.src; 93 | const srcset = img.srcset; 94 | const alt = img.alt; 95 | 96 | allPins[url] = { url, src, alt, srcset }; 97 | } else { 98 | console.log( 99 | "[archivist-pinterest-crawl]", 100 | "no a/img for", 101 | pin 102 | ); 103 | } 104 | } 105 | ); 106 | 107 | if (window.scrollY === lastScrollPosition) { 108 | resolve(Object.values(allPins)); 109 | } else { 110 | lastScrollPosition = window.scrollY; 111 | scrollDown(); 112 | } 113 | }, 10); 114 | }; 115 | 116 | scrollDown(); 117 | }) 118 | ); 119 | 120 | return scrollResult.map(pin => { 121 | return { 122 | ...pin, 123 | biggestSrc: chain(pin.srcset) 124 | .split(",") 125 | .last() 126 | .trim() 127 | .split(" ") 128 | .first() 129 | .value() 130 | }; 131 | }); 132 | }; 133 | 134 | const crawlProfile = async (page, profileUrl) => { 135 | console.log("[archivist-pinterest-crawl]", "crawling profile", profileUrl); 136 | 137 | await page.goto(profileUrl, { waitUntil: "networkidle2" }); 138 | 139 | const boards = await page.evaluate(() => { 140 | return Array.from(document.querySelectorAll("[draggable=true]")).map(el => { 141 | return el.querySelector("a").href; 142 | }); 143 | }); 144 | 145 | return boards; 146 | }; 147 | 148 | const loginWithCreds = async (page, email, password) => { 149 | await page.goto(ROOT, { waitUntil: "networkidle2" }); 150 | await page.click("[data-test-id=simple-login-button] > button"); 151 | 152 | await page.type("#email", email); 153 | await sleep(2000); 154 | await page.type("#password", password); 155 | await sleep(2000); 156 | 157 | await page.click(".SignupButton"); 158 | await page.waitForNavigation(); 159 | }; 160 | 161 | const loginWithCookiesFromChrome = async page => 162 | new Promise(resolve => { 163 | chrome.getCookies(ROOT, "puppeteer", (err, cookies) => { 164 | page.setCookie(...cookies).then(() => { 165 | page.goto(ROOT, { waitUntil: "networkidle2" }).then(() => { 166 | resolve(); 167 | }); 168 | }); 169 | }); 170 | }); 171 | 172 | const createBrowser = async options => { 173 | const headless = true; 174 | const browser = await puppeteer.launch({ headless }); 175 | 176 | const page = await browser.newPage(); 177 | await page.setViewport({ width: 1600, height: 900, deviceScaleRatio: 2 }); 178 | 179 | if (options.loginMethod === "cookies") { 180 | await loginWithCookiesFromChrome(page); 181 | } else if (options.loginMethod === "password") { 182 | await loginWithCreds(page, options.username, options.password); 183 | } else { 184 | throw new Error("invalid login option"); 185 | } 186 | 187 | assert(options.profile, "requires profile option"); 188 | 189 | return { browser, page }; 190 | }; 191 | 192 | const crawlBoards = async options => { 193 | const { browser, page } = await createBrowser(options); 194 | 195 | const boards = await crawlProfile(page, ROOT + "/" + options.profile + "/boards"); 196 | 197 | return new Promise(resolve => { 198 | async.mapSeries( 199 | boards, 200 | (board, callback) => 201 | crawlBoard(page, board).then(pins => { 202 | console.log( 203 | "[archivist-pinterest-crawl]", 204 | "board pins:", 205 | board, 206 | pins.length 207 | ); 208 | 209 | callback( 210 | null, 211 | pins.map(pin => ({ 212 | ...pin, 213 | board: chain(board) 214 | .split("/") 215 | .takeRight(2) 216 | .first() 217 | .value() 218 | })) 219 | ); 220 | }), 221 | (err, res) => { 222 | browser.close().then(() => { 223 | resolve(flatten(res)); 224 | }); 225 | } 226 | ); 227 | }); 228 | }; 229 | 230 | const crawlPinMetadata = async (options, pins) => { 231 | const { browser } = await createBrowser(options); 232 | 233 | return new Promise(resolve => { 234 | async.mapLimit( 235 | pins, 236 | 4, 237 | (pin, callback) => { 238 | crawlPin(browser, pin.url).then(({ link, title, date }) => { 239 | callback(null, { ...pin, title, link, createdAt: date }); 240 | }); 241 | }, 242 | (err, res) => { 243 | browser.close().then(() => { 244 | resolve(res); 245 | }); 246 | } 247 | ); 248 | }); 249 | }; 250 | 251 | module.exports = { crawlBoards, crawlPinMetadata }; 252 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/fetch/fetcher.js: -------------------------------------------------------------------------------- 1 | const async = require("async"); 2 | const envPaths = require("env-paths"); 3 | const fs = require("fs"); 4 | const md5 = require("md5"); 5 | const mkdirp = require("mkdirp"); 6 | const path = require("path"); 7 | const sizeOf = require("image-size"); 8 | const tmp = require("tmp"); 9 | const wget = require("node-wget"); 10 | 11 | const TMP_PATH = envPaths("archivist-pinterest").data; 12 | const DATA_PATH = envPaths("archivist-pinterest").data; 13 | const ASSETS_PATH = path.join(DATA_PATH, "assets"); 14 | 15 | mkdirp(TMP_PATH); 16 | mkdirp(DATA_PATH); 17 | mkdirp(ASSETS_PATH); 18 | 19 | const download = async url => { 20 | console.log("[archivist-pinterest-crawl]", "downloading", url); 21 | 22 | const tempPath = tmp.tmpNameSync(); 23 | 24 | return new Promise((resolve, reject) => 25 | wget({ url, dest: tempPath }, (error, result, body) => { 26 | if (error) { 27 | return reject(error); 28 | } 29 | 30 | const ext = path.extname(url); 31 | const hash = md5(body); 32 | const filename = `${hash}${ext}`; 33 | const finalPath = path.join(ASSETS_PATH, filename); 34 | 35 | fs.renameSync(tempPath, finalPath); 36 | 37 | sizeOf(finalPath, (err, size) => { 38 | if (err) { 39 | console.log( 40 | "[archivist-pinterest-crawl]", 41 | `image-size error: ${err} (${finalPath})` 42 | ); 43 | 44 | resolve({ filename, width: 0, height: 0 }); 45 | } else { 46 | resolve({ filename, ...size }); 47 | } 48 | }); 49 | }) 50 | ); 51 | }; 52 | 53 | module.exports = async crawledPins => { 54 | return new Promise(resolve => { 55 | async.mapLimit( 56 | crawledPins, 57 | 10, 58 | async pin => { 59 | const { filename, width, height } = await download(pin.biggestSrc); 60 | return { ...pin, filename, width, height }; 61 | }, 62 | (err, res) => { 63 | resolve(res); 64 | } 65 | ); 66 | }); 67 | }; 68 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/fetch/index.js: -------------------------------------------------------------------------------- 1 | const Database = require("better-sqlite3"); 2 | const async = require("async"); 3 | const dateFormat = require("dateformat"); 4 | const envPaths = require("env-paths"); 5 | const fs = require("fs"); 6 | const mkdirp = require("mkdirp"); 7 | const path = require("path"); 8 | const { chain } = require("lodash"); 9 | 10 | const { crawlBoards, crawlPinMetadata } = require("./crawler"); 11 | const fetcher = require("./fetcher"); 12 | 13 | const DATA_PATH = envPaths("archivist-pinterest").data; 14 | mkdirp(DATA_PATH); 15 | 16 | const CRAWLED_DATA_PATH = path.join(DATA_PATH, "crawled-pins.json"); 17 | 18 | const makePinId = pin => { 19 | return chain(pin.url) 20 | .split("/") 21 | .takeRight(2) 22 | .first() 23 | .value(); 24 | }; 25 | 26 | const processRemovedPins = async removedPins => { 27 | return new Promise(resolve => { 28 | async.mapLimit( 29 | removedPins, 30 | 10, 31 | (item, callback) => { 32 | const filePath = item.filename && path.join(DATA_PATH, item.filename); 33 | 34 | if (filePath && fs.existsSync(filePath)) { 35 | console.log("[archivist-pinterest-crawl]", `unlinking ${filePath}`); 36 | fs.unlinkSync(filePath); 37 | } 38 | 39 | callback(null, item.pinid); 40 | }, 41 | (err, pinids) => resolve(pinids) 42 | ); 43 | }); 44 | }; 45 | 46 | const run = async options => { 47 | const db = new Database(path.join(DATA_PATH, "data.db")); 48 | 49 | db.prepare( 50 | ` 51 | CREATE TABLE IF NOT EXISTS data ( 52 | board TEXT, 53 | filename TEXT, 54 | title TEXT, 55 | text TEXT, 56 | link TEXT, 57 | pinurl TEXT, 58 | width INTEGER, 59 | height INTEGER, 60 | pinid TEXT PRIMARY KEY, 61 | crawldate DATETIME, 62 | createdat DATETIME 63 | ) 64 | ` 65 | ).run(); 66 | 67 | const search = db.prepare( 68 | "SELECT count(pinid) AS count FROM data WHERE pinid = ?" 69 | ); 70 | 71 | const insert = db.prepare( 72 | `INSERT OR REPLACE INTO data (board, filename, title, text, link, pinurl, pinid, crawldate, createdat, width, height) 73 | VALUES (:board, :filename, :title, :text, :link, :pinurl, :pinid, :crawldate, :createdat, :width, :height)` 74 | ); 75 | 76 | const remove = db.prepare("DELETE FROM data WHERE pinid = ?"); 77 | 78 | const dbPins = db.prepare("SELECT * FROM data").all(); 79 | 80 | const crawledPins = await crawlBoards(options); 81 | // const crawledPins = require(CRAWLED_DATA_PATH); 82 | 83 | if (crawledPins.length === 0) { 84 | console.log("[archivist-pinterest-crawl]", "0 crawled pins, exiting"); 85 | return; 86 | } 87 | 88 | fs.writeFileSync( 89 | CRAWLED_DATA_PATH, 90 | JSON.stringify(crawledPins, null, 2), 91 | "utf-8" 92 | ); 93 | // console.log("[archivist-pinterest-crawl]", `crawled data saved to ${CRAWLED_DATA_PATH}`); 94 | 95 | const newPins = crawledPins.filter(pin => { 96 | if (!pin) { 97 | return false; 98 | } 99 | 100 | const pinid = makePinId(pin); 101 | return search.get(pinid).count === 0; 102 | }); 103 | 104 | const removedPins = dbPins.filter( 105 | ({ pinid }) => !crawledPins.find(pin => makePinId(pin) === pinid) 106 | ); 107 | 108 | console.log( 109 | "[archivist-pinterest-crawl]", 110 | `all pins: ${crawledPins.length} / new pins: ${newPins.length} / removed pins: ${removedPins.length}` 111 | ); 112 | 113 | const pinidsToRemove = await processRemovedPins(removedPins); 114 | 115 | const removePins = db.transaction(pinids => { 116 | pinids.forEach(pinid => remove.run(pinid)); 117 | }); 118 | 119 | removePins(pinidsToRemove); 120 | 121 | const newPinsWithMetadata = await crawlPinMetadata(options, newPins); 122 | 123 | const fetchedPins = await fetcher(newPinsWithMetadata); 124 | 125 | const crawldate = dateFormat(new Date(), "isoDateTime"); 126 | 127 | const finalPins = fetchedPins.map(pin => ({ 128 | board: pin.board, 129 | filename: pin.filename, 130 | title: pin.title, 131 | text: pin.alt, 132 | link: pin.link, 133 | pinurl: pin.url, 134 | pinid: makePinId(pin), 135 | crawldate, 136 | createdat: pin.createdAt 137 | ? dateFormat(new Date(pin.createdAt), "isoDateTime") 138 | : undefined, 139 | width: pin.width, 140 | height: pin.height 141 | })); 142 | 143 | const insertPins = db.transaction(pins => { 144 | pins.forEach(pin => insert.run(pin)); 145 | }); 146 | 147 | insertPins(finalPins); 148 | 149 | console.log( 150 | "[archivist-pinterest-crawl]", 151 | `inserted pins: ${finalPins.length} (of ${newPins.length})` 152 | ); 153 | }; 154 | 155 | module.exports = run; 156 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/index.js: -------------------------------------------------------------------------------- 1 | const fetch = require("./fetch"); 2 | const query = require("./query"); 3 | 4 | module.exports = options => ({ 5 | fetch: () => fetch(options), 6 | get: () => query(options), 7 | query: text => query(options, text) 8 | }); 9 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-pinterest-crawl", 3 | "version": "1.2.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/mime-types": { 8 | "version": "2.1.0", 9 | "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", 10 | "integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=" 11 | }, 12 | "@types/node": { 13 | "version": "12.12.14", 14 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz", 15 | "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==" 16 | }, 17 | "@types/yauzl": { 18 | "version": "2.9.1", 19 | "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", 20 | "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", 21 | "optional": true, 22 | "requires": { 23 | "@types/node": "*" 24 | } 25 | }, 26 | "abbrev": { 27 | "version": "1.1.1", 28 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 29 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 30 | }, 31 | "agent-base": { 32 | "version": "5.1.1", 33 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", 34 | "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" 35 | }, 36 | "ajv": { 37 | "version": "6.12.2", 38 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", 39 | "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", 40 | "requires": { 41 | "fast-deep-equal": "^3.1.1", 42 | "fast-json-stable-stringify": "^2.0.0", 43 | "json-schema-traverse": "^0.4.1", 44 | "uri-js": "^4.2.2" 45 | } 46 | }, 47 | "ansi-regex": { 48 | "version": "2.1.1", 49 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 50 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 51 | }, 52 | "aproba": { 53 | "version": "1.2.0", 54 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 55 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 56 | }, 57 | "are-we-there-yet": { 58 | "version": "1.1.5", 59 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 60 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 61 | "requires": { 62 | "delegates": "^1.0.0", 63 | "readable-stream": "^2.0.6" 64 | } 65 | }, 66 | "asn1": { 67 | "version": "0.2.4", 68 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 69 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 70 | "requires": { 71 | "safer-buffer": "~2.1.0" 72 | } 73 | }, 74 | "assert-plus": { 75 | "version": "1.0.0", 76 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 77 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 78 | }, 79 | "async": { 80 | "version": "3.2.0", 81 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", 82 | "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" 83 | }, 84 | "asynckit": { 85 | "version": "0.4.0", 86 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 87 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 88 | }, 89 | "aws-sign2": { 90 | "version": "0.7.0", 91 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 92 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 93 | }, 94 | "aws4": { 95 | "version": "1.9.0", 96 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", 97 | "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" 98 | }, 99 | "balanced-match": { 100 | "version": "1.0.0", 101 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 102 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 103 | }, 104 | "base64-js": { 105 | "version": "1.3.1", 106 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 107 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 108 | }, 109 | "bcrypt-pbkdf": { 110 | "version": "1.0.2", 111 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 112 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 113 | "requires": { 114 | "tweetnacl": "^0.14.3" 115 | } 116 | }, 117 | "better-sqlite3": { 118 | "version": "6.0.1", 119 | "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-6.0.1.tgz", 120 | "integrity": "sha512-4aV1zEknM9g1a6B0mVBx1oIlmYioEJ8gSS3J6EpN1b1bKYEE+N5lmpmXHKNKTi0qjHziSd7XrXwHl1kpqvEcHQ==", 121 | "requires": { 122 | "bindings": "^1.5.0", 123 | "integer": "^3.0.1", 124 | "prebuild-install": "^5.3.3", 125 | "tar": "4.4.10" 126 | } 127 | }, 128 | "bindings": { 129 | "version": "1.5.0", 130 | "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", 131 | "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", 132 | "requires": { 133 | "file-uri-to-path": "1.0.0" 134 | } 135 | }, 136 | "bl": { 137 | "version": "4.0.2", 138 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", 139 | "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", 140 | "requires": { 141 | "buffer": "^5.5.0", 142 | "inherits": "^2.0.4", 143 | "readable-stream": "^3.4.0" 144 | }, 145 | "dependencies": { 146 | "readable-stream": { 147 | "version": "3.6.0", 148 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 149 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 150 | "requires": { 151 | "inherits": "^2.0.3", 152 | "string_decoder": "^1.1.1", 153 | "util-deprecate": "^1.0.1" 154 | } 155 | } 156 | } 157 | }, 158 | "boolbase": { 159 | "version": "1.0.0", 160 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 161 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 162 | }, 163 | "boom": { 164 | "version": "4.3.1", 165 | "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", 166 | "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", 167 | "requires": { 168 | "hoek": "4.x.x" 169 | } 170 | }, 171 | "brace-expansion": { 172 | "version": "1.1.11", 173 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 174 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 175 | "requires": { 176 | "balanced-match": "^1.0.0", 177 | "concat-map": "0.0.1" 178 | } 179 | }, 180 | "buffer": { 181 | "version": "5.6.0", 182 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 183 | "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 184 | "requires": { 185 | "base64-js": "^1.0.2", 186 | "ieee754": "^1.1.4" 187 | } 188 | }, 189 | "buffer-crc32": { 190 | "version": "0.2.13", 191 | "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", 192 | "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" 193 | }, 194 | "caseless": { 195 | "version": "0.12.0", 196 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 197 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 198 | }, 199 | "charenc": { 200 | "version": "0.0.2", 201 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 202 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" 203 | }, 204 | "cheerio": { 205 | "version": "1.0.0-rc.3", 206 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", 207 | "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", 208 | "requires": { 209 | "css-select": "~1.2.0", 210 | "dom-serializer": "~0.1.1", 211 | "entities": "~1.1.1", 212 | "htmlparser2": "^3.9.1", 213 | "lodash": "^4.15.0", 214 | "parse5": "^3.0.1" 215 | } 216 | }, 217 | "chownr": { 218 | "version": "1.1.4", 219 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 220 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" 221 | }, 222 | "chrome-cookies-secure": { 223 | "version": "1.3.2", 224 | "resolved": "https://registry.npmjs.org/chrome-cookies-secure/-/chrome-cookies-secure-1.3.2.tgz", 225 | "integrity": "sha512-HA3dmbZPiK7/IEA457UOOrZu7DYwCqAb/3HaXirg+qLNVTeyNYVcAIs1INY2mqOeEAlN5vUls1G9Ub9EJJ4NGw==", 226 | "requires": { 227 | "int": "^0.2.0", 228 | "keytar": "^5.0.0", 229 | "request": "^2.88.0", 230 | "sqlite3": "^4.1.1", 231 | "tldjs": "^1.5.1", 232 | "tough-cookie": "^2.3.4", 233 | "win-dpapi": "^0.1.0" 234 | } 235 | }, 236 | "co": { 237 | "version": "4.6.0", 238 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 239 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 240 | }, 241 | "code-point-at": { 242 | "version": "1.1.0", 243 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 244 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 245 | }, 246 | "combined-stream": { 247 | "version": "1.0.8", 248 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 249 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 250 | "requires": { 251 | "delayed-stream": "~1.0.0" 252 | } 253 | }, 254 | "concat-map": { 255 | "version": "0.0.1", 256 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 257 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 258 | }, 259 | "console-control-strings": { 260 | "version": "1.1.0", 261 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 262 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 263 | }, 264 | "core-util-is": { 265 | "version": "1.0.2", 266 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 267 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 268 | }, 269 | "crypt": { 270 | "version": "0.0.2", 271 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 272 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" 273 | }, 274 | "cryptiles": { 275 | "version": "3.1.4", 276 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.4.tgz", 277 | "integrity": "sha512-8I1sgZHfVwcSOY6mSGpVU3lw/GSIZvusg8dD2+OGehCJpOhQRLNcH0qb9upQnOH4XhgxxFJSg6E2kx95deb1Tw==", 278 | "requires": { 279 | "boom": "5.x.x" 280 | }, 281 | "dependencies": { 282 | "boom": { 283 | "version": "5.2.0", 284 | "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", 285 | "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", 286 | "requires": { 287 | "hoek": "4.x.x" 288 | } 289 | } 290 | } 291 | }, 292 | "css-select": { 293 | "version": "1.2.0", 294 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 295 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", 296 | "requires": { 297 | "boolbase": "~1.0.0", 298 | "css-what": "2.1", 299 | "domutils": "1.5.1", 300 | "nth-check": "~1.0.1" 301 | } 302 | }, 303 | "css-what": { 304 | "version": "2.1.3", 305 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", 306 | "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" 307 | }, 308 | "dashdash": { 309 | "version": "1.14.1", 310 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 311 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 312 | "requires": { 313 | "assert-plus": "^1.0.0" 314 | } 315 | }, 316 | "dateformat": { 317 | "version": "3.0.3", 318 | "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", 319 | "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" 320 | }, 321 | "debug": { 322 | "version": "3.2.6", 323 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 324 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 325 | "requires": { 326 | "ms": "^2.1.1" 327 | } 328 | }, 329 | "decompress-response": { 330 | "version": "4.2.1", 331 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", 332 | "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", 333 | "requires": { 334 | "mimic-response": "^2.0.0" 335 | } 336 | }, 337 | "deep-extend": { 338 | "version": "0.6.0", 339 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 340 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 341 | }, 342 | "delayed-stream": { 343 | "version": "1.0.0", 344 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 345 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 346 | }, 347 | "delegates": { 348 | "version": "1.0.0", 349 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 350 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 351 | }, 352 | "detect-libc": { 353 | "version": "1.0.3", 354 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 355 | "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 356 | }, 357 | "dom-serializer": { 358 | "version": "0.1.1", 359 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", 360 | "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", 361 | "requires": { 362 | "domelementtype": "^1.3.0", 363 | "entities": "^1.1.1" 364 | } 365 | }, 366 | "domelementtype": { 367 | "version": "1.3.1", 368 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", 369 | "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" 370 | }, 371 | "domhandler": { 372 | "version": "2.4.2", 373 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", 374 | "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", 375 | "requires": { 376 | "domelementtype": "1" 377 | } 378 | }, 379 | "domutils": { 380 | "version": "1.5.1", 381 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 382 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 383 | "requires": { 384 | "dom-serializer": "0", 385 | "domelementtype": "1" 386 | } 387 | }, 388 | "ecc-jsbn": { 389 | "version": "0.1.2", 390 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 391 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 392 | "requires": { 393 | "jsbn": "~0.1.0", 394 | "safer-buffer": "^2.1.0" 395 | } 396 | }, 397 | "end-of-stream": { 398 | "version": "1.4.4", 399 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 400 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 401 | "requires": { 402 | "once": "^1.4.0" 403 | } 404 | }, 405 | "entities": { 406 | "version": "1.1.2", 407 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 408 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" 409 | }, 410 | "env-paths": { 411 | "version": "2.2.0", 412 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", 413 | "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" 414 | }, 415 | "expand-template": { 416 | "version": "2.0.3", 417 | "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 418 | "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" 419 | }, 420 | "extend": { 421 | "version": "3.0.2", 422 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 423 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 424 | }, 425 | "extract-zip": { 426 | "version": "2.0.0", 427 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", 428 | "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", 429 | "requires": { 430 | "@types/yauzl": "^2.9.1", 431 | "debug": "^4.1.1", 432 | "get-stream": "^5.1.0", 433 | "yauzl": "^2.10.0" 434 | }, 435 | "dependencies": { 436 | "debug": { 437 | "version": "4.1.1", 438 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 439 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 440 | "requires": { 441 | "ms": "^2.1.1" 442 | } 443 | } 444 | } 445 | }, 446 | "extsprintf": { 447 | "version": "1.3.0", 448 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 449 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 450 | }, 451 | "fast-deep-equal": { 452 | "version": "3.1.1", 453 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", 454 | "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" 455 | }, 456 | "fast-json-stable-stringify": { 457 | "version": "2.0.0", 458 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 459 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 460 | }, 461 | "fd-slicer": { 462 | "version": "1.1.0", 463 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", 464 | "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", 465 | "requires": { 466 | "pend": "~1.2.0" 467 | } 468 | }, 469 | "file-uri-to-path": { 470 | "version": "1.0.0", 471 | "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", 472 | "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" 473 | }, 474 | "forever-agent": { 475 | "version": "0.6.1", 476 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 477 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 478 | }, 479 | "form-data": { 480 | "version": "2.3.3", 481 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 482 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 483 | "requires": { 484 | "asynckit": "^0.4.0", 485 | "combined-stream": "^1.0.6", 486 | "mime-types": "^2.1.12" 487 | } 488 | }, 489 | "fs-constants": { 490 | "version": "1.0.0", 491 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 492 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 493 | }, 494 | "fs-minipass": { 495 | "version": "1.2.7", 496 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", 497 | "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", 498 | "requires": { 499 | "minipass": "^2.6.0" 500 | } 501 | }, 502 | "fs.realpath": { 503 | "version": "1.0.0", 504 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 505 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 506 | }, 507 | "gauge": { 508 | "version": "2.7.4", 509 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 510 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 511 | "requires": { 512 | "aproba": "^1.0.3", 513 | "console-control-strings": "^1.0.0", 514 | "has-unicode": "^2.0.0", 515 | "object-assign": "^4.1.0", 516 | "signal-exit": "^3.0.0", 517 | "string-width": "^1.0.1", 518 | "strip-ansi": "^3.0.1", 519 | "wide-align": "^1.1.0" 520 | } 521 | }, 522 | "get-stream": { 523 | "version": "5.1.0", 524 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", 525 | "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", 526 | "requires": { 527 | "pump": "^3.0.0" 528 | } 529 | }, 530 | "getpass": { 531 | "version": "0.1.7", 532 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 533 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 534 | "requires": { 535 | "assert-plus": "^1.0.0" 536 | } 537 | }, 538 | "github-from-package": { 539 | "version": "0.0.0", 540 | "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 541 | "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" 542 | }, 543 | "glob": { 544 | "version": "7.1.6", 545 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 546 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 547 | "requires": { 548 | "fs.realpath": "^1.0.0", 549 | "inflight": "^1.0.4", 550 | "inherits": "2", 551 | "minimatch": "^3.0.4", 552 | "once": "^1.3.0", 553 | "path-is-absolute": "^1.0.0" 554 | } 555 | }, 556 | "har-schema": { 557 | "version": "2.0.0", 558 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 559 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 560 | }, 561 | "har-validator": { 562 | "version": "5.1.3", 563 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 564 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 565 | "requires": { 566 | "ajv": "^6.5.5", 567 | "har-schema": "^2.0.0" 568 | } 569 | }, 570 | "has-unicode": { 571 | "version": "2.0.1", 572 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 573 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 574 | }, 575 | "hawk": { 576 | "version": "6.0.2", 577 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", 578 | "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", 579 | "requires": { 580 | "boom": "4.x.x", 581 | "cryptiles": "3.x.x", 582 | "hoek": "4.x.x", 583 | "sntp": "2.x.x" 584 | } 585 | }, 586 | "hoek": { 587 | "version": "4.2.1", 588 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", 589 | "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" 590 | }, 591 | "htmlparser2": { 592 | "version": "3.10.1", 593 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", 594 | "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", 595 | "requires": { 596 | "domelementtype": "^1.3.1", 597 | "domhandler": "^2.3.0", 598 | "domutils": "^1.5.1", 599 | "entities": "^1.1.1", 600 | "inherits": "^2.0.1", 601 | "readable-stream": "^3.1.1" 602 | }, 603 | "dependencies": { 604 | "readable-stream": { 605 | "version": "3.4.0", 606 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", 607 | "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", 608 | "requires": { 609 | "inherits": "^2.0.3", 610 | "string_decoder": "^1.1.1", 611 | "util-deprecate": "^1.0.1" 612 | } 613 | } 614 | } 615 | }, 616 | "http-signature": { 617 | "version": "1.2.0", 618 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 619 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 620 | "requires": { 621 | "assert-plus": "^1.0.0", 622 | "jsprim": "^1.2.2", 623 | "sshpk": "^1.7.0" 624 | } 625 | }, 626 | "https-proxy-agent": { 627 | "version": "4.0.0", 628 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", 629 | "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", 630 | "requires": { 631 | "agent-base": "5", 632 | "debug": "4" 633 | }, 634 | "dependencies": { 635 | "debug": { 636 | "version": "4.1.1", 637 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 638 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 639 | "requires": { 640 | "ms": "^2.1.1" 641 | } 642 | } 643 | } 644 | }, 645 | "iconv-lite": { 646 | "version": "0.4.24", 647 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 648 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 649 | "requires": { 650 | "safer-buffer": ">= 2.1.2 < 3" 651 | } 652 | }, 653 | "ieee754": { 654 | "version": "1.1.13", 655 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 656 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 657 | }, 658 | "ignore-walk": { 659 | "version": "3.0.3", 660 | "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", 661 | "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", 662 | "requires": { 663 | "minimatch": "^3.0.4" 664 | } 665 | }, 666 | "image-size": { 667 | "version": "0.8.3", 668 | "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz", 669 | "integrity": "sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==", 670 | "requires": { 671 | "queue": "6.0.1" 672 | } 673 | }, 674 | "inflight": { 675 | "version": "1.0.6", 676 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 677 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 678 | "requires": { 679 | "once": "^1.3.0", 680 | "wrappy": "1" 681 | } 682 | }, 683 | "inherits": { 684 | "version": "2.0.4", 685 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 686 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 687 | }, 688 | "ini": { 689 | "version": "1.3.5", 690 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 691 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 692 | }, 693 | "int": { 694 | "version": "0.2.0", 695 | "resolved": "https://registry.npmjs.org/int/-/int-0.2.0.tgz", 696 | "integrity": "sha1-WJ8FsDuNjAjJGMiIR4TLYqlO9H4=" 697 | }, 698 | "integer": { 699 | "version": "3.0.1", 700 | "resolved": "https://registry.npmjs.org/integer/-/integer-3.0.1.tgz", 701 | "integrity": "sha512-OqtER6W2GIJTIcnT5o2B/pWGgvurnVOYs4OZCgay40QEIbMTnNq4R0KSaIw1TZyFtPWjm5aNM+pBBMTfc3exmw==", 702 | "requires": { 703 | "bindings": "^1.5.0", 704 | "prebuild-install": "^5.3.3" 705 | } 706 | }, 707 | "is-buffer": { 708 | "version": "1.1.6", 709 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 710 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 711 | }, 712 | "is-fullwidth-code-point": { 713 | "version": "1.0.0", 714 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 715 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 716 | "requires": { 717 | "number-is-nan": "^1.0.0" 718 | } 719 | }, 720 | "is-typedarray": { 721 | "version": "1.0.0", 722 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 723 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 724 | }, 725 | "isarray": { 726 | "version": "1.0.0", 727 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 728 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 729 | }, 730 | "isstream": { 731 | "version": "0.1.2", 732 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 733 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 734 | }, 735 | "jsbn": { 736 | "version": "0.1.1", 737 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 738 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 739 | }, 740 | "json-schema": { 741 | "version": "0.2.3", 742 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 743 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 744 | }, 745 | "json-schema-traverse": { 746 | "version": "0.4.1", 747 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 748 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 749 | }, 750 | "json-stringify-safe": { 751 | "version": "5.0.1", 752 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 753 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 754 | }, 755 | "jsprim": { 756 | "version": "1.4.1", 757 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 758 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 759 | "requires": { 760 | "assert-plus": "1.0.0", 761 | "extsprintf": "1.3.0", 762 | "json-schema": "0.2.3", 763 | "verror": "1.10.0" 764 | } 765 | }, 766 | "keytar": { 767 | "version": "5.5.0", 768 | "resolved": "https://registry.npmjs.org/keytar/-/keytar-5.5.0.tgz", 769 | "integrity": "sha512-1d/F2qAL/qijpm25wNq8eez4mE+/J4eBvqyLfspIYIeCEHn3nttFwplowIZfbdNCjFGWh68MzGZOd2vS61Ffew==", 770 | "optional": true, 771 | "requires": { 772 | "nan": "2.14.0", 773 | "prebuild-install": "5.3.3" 774 | } 775 | }, 776 | "lodash": { 777 | "version": "4.17.15", 778 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 779 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 780 | }, 781 | "md5": { 782 | "version": "2.2.1", 783 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", 784 | "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", 785 | "requires": { 786 | "charenc": "~0.0.1", 787 | "crypt": "~0.0.1", 788 | "is-buffer": "~1.1.1" 789 | } 790 | }, 791 | "mime": { 792 | "version": "2.4.4", 793 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", 794 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" 795 | }, 796 | "mime-db": { 797 | "version": "1.42.0", 798 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", 799 | "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" 800 | }, 801 | "mime-types": { 802 | "version": "2.1.25", 803 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", 804 | "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", 805 | "requires": { 806 | "mime-db": "1.42.0" 807 | } 808 | }, 809 | "mimic-response": { 810 | "version": "2.1.0", 811 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", 812 | "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" 813 | }, 814 | "minimatch": { 815 | "version": "3.0.4", 816 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 817 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 818 | "requires": { 819 | "brace-expansion": "^1.1.7" 820 | } 821 | }, 822 | "minimist": { 823 | "version": "1.2.5", 824 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 825 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 826 | }, 827 | "minipass": { 828 | "version": "2.9.0", 829 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", 830 | "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", 831 | "requires": { 832 | "safe-buffer": "^5.1.2", 833 | "yallist": "^3.0.0" 834 | } 835 | }, 836 | "minizlib": { 837 | "version": "1.3.3", 838 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", 839 | "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", 840 | "requires": { 841 | "minipass": "^2.9.0" 842 | } 843 | }, 844 | "mkdirp": { 845 | "version": "1.0.4", 846 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 847 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" 848 | }, 849 | "mkdirp-classic": { 850 | "version": "0.5.2", 851 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", 852 | "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==" 853 | }, 854 | "ms": { 855 | "version": "2.1.2", 856 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 857 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 858 | }, 859 | "nan": { 860 | "version": "2.14.0", 861 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 862 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" 863 | }, 864 | "napi-build-utils": { 865 | "version": "1.0.2", 866 | "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", 867 | "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" 868 | }, 869 | "needle": { 870 | "version": "2.4.1", 871 | "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz", 872 | "integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==", 873 | "requires": { 874 | "debug": "^3.2.6", 875 | "iconv-lite": "^0.4.4", 876 | "sax": "^1.2.4" 877 | } 878 | }, 879 | "node-abi": { 880 | "version": "2.16.0", 881 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz", 882 | "integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==", 883 | "requires": { 884 | "semver": "^5.4.1" 885 | } 886 | }, 887 | "node-pre-gyp": { 888 | "version": "0.11.0", 889 | "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", 890 | "integrity": "sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==", 891 | "requires": { 892 | "detect-libc": "^1.0.2", 893 | "mkdirp": "^0.5.1", 894 | "needle": "^2.2.1", 895 | "nopt": "^4.0.1", 896 | "npm-packlist": "^1.1.6", 897 | "npmlog": "^4.0.2", 898 | "rc": "^1.2.7", 899 | "rimraf": "^2.6.1", 900 | "semver": "^5.3.0", 901 | "tar": "^4" 902 | }, 903 | "dependencies": { 904 | "mkdirp": { 905 | "version": "0.5.5", 906 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 907 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 908 | "requires": { 909 | "minimist": "^1.2.5" 910 | } 911 | } 912 | } 913 | }, 914 | "node-wget": { 915 | "version": "0.4.3", 916 | "resolved": "https://registry.npmjs.org/node-wget/-/node-wget-0.4.3.tgz", 917 | "integrity": "sha512-sltt/nc4NJeI4CuncyBFZZ7W4Wz3Q1oM8h27mYEbj5OihXMsuqJpplPl0WUZTSpXy8AyGczT08jIBsXHxdCtgg==", 918 | "requires": { 919 | "request": "~2.85.0" 920 | }, 921 | "dependencies": { 922 | "ajv": { 923 | "version": "5.5.2", 924 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 925 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 926 | "requires": { 927 | "co": "^4.6.0", 928 | "fast-deep-equal": "^1.0.0", 929 | "fast-json-stable-stringify": "^2.0.0", 930 | "json-schema-traverse": "^0.3.0" 931 | } 932 | }, 933 | "fast-deep-equal": { 934 | "version": "1.1.0", 935 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 936 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 937 | }, 938 | "har-validator": { 939 | "version": "5.0.3", 940 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", 941 | "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", 942 | "requires": { 943 | "ajv": "^5.1.0", 944 | "har-schema": "^2.0.0" 945 | } 946 | }, 947 | "json-schema-traverse": { 948 | "version": "0.3.1", 949 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 950 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 951 | }, 952 | "oauth-sign": { 953 | "version": "0.8.2", 954 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 955 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 956 | }, 957 | "punycode": { 958 | "version": "1.4.1", 959 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 960 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 961 | }, 962 | "request": { 963 | "version": "2.85.0", 964 | "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", 965 | "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", 966 | "requires": { 967 | "aws-sign2": "~0.7.0", 968 | "aws4": "^1.6.0", 969 | "caseless": "~0.12.0", 970 | "combined-stream": "~1.0.5", 971 | "extend": "~3.0.1", 972 | "forever-agent": "~0.6.1", 973 | "form-data": "~2.3.1", 974 | "har-validator": "~5.0.3", 975 | "hawk": "~6.0.2", 976 | "http-signature": "~1.2.0", 977 | "is-typedarray": "~1.0.0", 978 | "isstream": "~0.1.2", 979 | "json-stringify-safe": "~5.0.1", 980 | "mime-types": "~2.1.17", 981 | "oauth-sign": "~0.8.2", 982 | "performance-now": "^2.1.0", 983 | "qs": "~6.5.1", 984 | "safe-buffer": "^5.1.1", 985 | "stringstream": "~0.0.5", 986 | "tough-cookie": "~2.3.3", 987 | "tunnel-agent": "^0.6.0", 988 | "uuid": "^3.1.0" 989 | } 990 | }, 991 | "tough-cookie": { 992 | "version": "2.3.4", 993 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", 994 | "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", 995 | "requires": { 996 | "punycode": "^1.4.1" 997 | } 998 | } 999 | } 1000 | }, 1001 | "noop-logger": { 1002 | "version": "0.1.1", 1003 | "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", 1004 | "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" 1005 | }, 1006 | "nopt": { 1007 | "version": "4.0.3", 1008 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", 1009 | "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", 1010 | "requires": { 1011 | "abbrev": "1", 1012 | "osenv": "^0.1.4" 1013 | } 1014 | }, 1015 | "npm-bundled": { 1016 | "version": "1.1.1", 1017 | "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", 1018 | "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", 1019 | "requires": { 1020 | "npm-normalize-package-bin": "^1.0.1" 1021 | } 1022 | }, 1023 | "npm-normalize-package-bin": { 1024 | "version": "1.0.1", 1025 | "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", 1026 | "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" 1027 | }, 1028 | "npm-packlist": { 1029 | "version": "1.4.8", 1030 | "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", 1031 | "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", 1032 | "requires": { 1033 | "ignore-walk": "^3.0.1", 1034 | "npm-bundled": "^1.0.1", 1035 | "npm-normalize-package-bin": "^1.0.1" 1036 | } 1037 | }, 1038 | "npmlog": { 1039 | "version": "4.1.2", 1040 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 1041 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 1042 | "requires": { 1043 | "are-we-there-yet": "~1.1.2", 1044 | "console-control-strings": "~1.1.0", 1045 | "gauge": "~2.7.3", 1046 | "set-blocking": "~2.0.0" 1047 | } 1048 | }, 1049 | "nth-check": { 1050 | "version": "1.0.2", 1051 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", 1052 | "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", 1053 | "requires": { 1054 | "boolbase": "~1.0.0" 1055 | } 1056 | }, 1057 | "number-is-nan": { 1058 | "version": "1.0.1", 1059 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1060 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1061 | }, 1062 | "oauth-sign": { 1063 | "version": "0.9.0", 1064 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 1065 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 1066 | }, 1067 | "object-assign": { 1068 | "version": "4.1.1", 1069 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1070 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1071 | }, 1072 | "once": { 1073 | "version": "1.4.0", 1074 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1075 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1076 | "requires": { 1077 | "wrappy": "1" 1078 | } 1079 | }, 1080 | "os-homedir": { 1081 | "version": "1.0.2", 1082 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1083 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 1084 | }, 1085 | "os-tmpdir": { 1086 | "version": "1.0.2", 1087 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1088 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1089 | }, 1090 | "osenv": { 1091 | "version": "0.1.5", 1092 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 1093 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 1094 | "requires": { 1095 | "os-homedir": "^1.0.0", 1096 | "os-tmpdir": "^1.0.0" 1097 | } 1098 | }, 1099 | "parse5": { 1100 | "version": "3.0.3", 1101 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", 1102 | "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", 1103 | "requires": { 1104 | "@types/node": "*" 1105 | } 1106 | }, 1107 | "path-is-absolute": { 1108 | "version": "1.0.1", 1109 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1110 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1111 | }, 1112 | "pend": { 1113 | "version": "1.2.0", 1114 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 1115 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" 1116 | }, 1117 | "performance-now": { 1118 | "version": "2.1.0", 1119 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1120 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1121 | }, 1122 | "prebuild-install": { 1123 | "version": "5.3.3", 1124 | "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", 1125 | "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==", 1126 | "requires": { 1127 | "detect-libc": "^1.0.3", 1128 | "expand-template": "^2.0.3", 1129 | "github-from-package": "0.0.0", 1130 | "minimist": "^1.2.0", 1131 | "mkdirp": "^0.5.1", 1132 | "napi-build-utils": "^1.0.1", 1133 | "node-abi": "^2.7.0", 1134 | "noop-logger": "^0.1.1", 1135 | "npmlog": "^4.0.1", 1136 | "pump": "^3.0.0", 1137 | "rc": "^1.2.7", 1138 | "simple-get": "^3.0.3", 1139 | "tar-fs": "^2.0.0", 1140 | "tunnel-agent": "^0.6.0", 1141 | "which-pm-runs": "^1.0.0" 1142 | }, 1143 | "dependencies": { 1144 | "mkdirp": { 1145 | "version": "0.5.5", 1146 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1147 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1148 | "requires": { 1149 | "minimist": "^1.2.5" 1150 | } 1151 | } 1152 | } 1153 | }, 1154 | "process-nextick-args": { 1155 | "version": "2.0.1", 1156 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 1157 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 1158 | }, 1159 | "progress": { 1160 | "version": "2.0.3", 1161 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1162 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" 1163 | }, 1164 | "proxy-from-env": { 1165 | "version": "1.1.0", 1166 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1167 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 1168 | }, 1169 | "psl": { 1170 | "version": "1.8.0", 1171 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", 1172 | "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" 1173 | }, 1174 | "pump": { 1175 | "version": "3.0.0", 1176 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1177 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1178 | "requires": { 1179 | "end-of-stream": "^1.1.0", 1180 | "once": "^1.3.1" 1181 | } 1182 | }, 1183 | "punycode": { 1184 | "version": "2.1.1", 1185 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1186 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1187 | }, 1188 | "puppeteer": { 1189 | "version": "3.0.1", 1190 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-3.0.1.tgz", 1191 | "integrity": "sha512-DxNnI9n4grVHC+9irUfNK2T6YFuRECJnvG7VzdVolxpVwWC5DQqI5ho9Z0af48K5MQW4sJY5cq3qQ5g6NkAjvw==", 1192 | "requires": { 1193 | "@types/mime-types": "^2.1.0", 1194 | "debug": "^4.1.0", 1195 | "extract-zip": "^2.0.0", 1196 | "https-proxy-agent": "^4.0.0", 1197 | "mime": "^2.0.3", 1198 | "mime-types": "^2.1.25", 1199 | "progress": "^2.0.1", 1200 | "proxy-from-env": "^1.0.0", 1201 | "rimraf": "^3.0.2", 1202 | "tar-fs": "^2.0.0", 1203 | "unbzip2-stream": "^1.3.3", 1204 | "ws": "^7.2.3" 1205 | }, 1206 | "dependencies": { 1207 | "debug": { 1208 | "version": "4.1.1", 1209 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 1210 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 1211 | "requires": { 1212 | "ms": "^2.1.1" 1213 | } 1214 | }, 1215 | "rimraf": { 1216 | "version": "3.0.2", 1217 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1218 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1219 | "requires": { 1220 | "glob": "^7.1.3" 1221 | } 1222 | } 1223 | } 1224 | }, 1225 | "qs": { 1226 | "version": "6.5.2", 1227 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1228 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1229 | }, 1230 | "queue": { 1231 | "version": "6.0.1", 1232 | "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz", 1233 | "integrity": "sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==", 1234 | "requires": { 1235 | "inherits": "~2.0.3" 1236 | } 1237 | }, 1238 | "rc": { 1239 | "version": "1.2.8", 1240 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1241 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1242 | "requires": { 1243 | "deep-extend": "^0.6.0", 1244 | "ini": "~1.3.0", 1245 | "minimist": "^1.2.0", 1246 | "strip-json-comments": "~2.0.1" 1247 | } 1248 | }, 1249 | "readable-stream": { 1250 | "version": "2.3.7", 1251 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 1252 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 1253 | "requires": { 1254 | "core-util-is": "~1.0.0", 1255 | "inherits": "~2.0.3", 1256 | "isarray": "~1.0.0", 1257 | "process-nextick-args": "~2.0.0", 1258 | "safe-buffer": "~5.1.1", 1259 | "string_decoder": "~1.1.1", 1260 | "util-deprecate": "~1.0.1" 1261 | } 1262 | }, 1263 | "request": { 1264 | "version": "2.88.2", 1265 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 1266 | "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 1267 | "requires": { 1268 | "aws-sign2": "~0.7.0", 1269 | "aws4": "^1.8.0", 1270 | "caseless": "~0.12.0", 1271 | "combined-stream": "~1.0.6", 1272 | "extend": "~3.0.2", 1273 | "forever-agent": "~0.6.1", 1274 | "form-data": "~2.3.2", 1275 | "har-validator": "~5.1.3", 1276 | "http-signature": "~1.2.0", 1277 | "is-typedarray": "~1.0.0", 1278 | "isstream": "~0.1.2", 1279 | "json-stringify-safe": "~5.0.1", 1280 | "mime-types": "~2.1.19", 1281 | "oauth-sign": "~0.9.0", 1282 | "performance-now": "^2.1.0", 1283 | "qs": "~6.5.2", 1284 | "safe-buffer": "^5.1.2", 1285 | "tough-cookie": "~2.5.0", 1286 | "tunnel-agent": "^0.6.0", 1287 | "uuid": "^3.3.2" 1288 | } 1289 | }, 1290 | "rimraf": { 1291 | "version": "2.7.1", 1292 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 1293 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 1294 | "requires": { 1295 | "glob": "^7.1.3" 1296 | } 1297 | }, 1298 | "safe-buffer": { 1299 | "version": "5.1.2", 1300 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1301 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1302 | }, 1303 | "safer-buffer": { 1304 | "version": "2.1.2", 1305 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1306 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1307 | }, 1308 | "sax": { 1309 | "version": "1.2.4", 1310 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 1311 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 1312 | }, 1313 | "semver": { 1314 | "version": "5.7.1", 1315 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1316 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1317 | }, 1318 | "set-blocking": { 1319 | "version": "2.0.0", 1320 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1321 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1322 | }, 1323 | "signal-exit": { 1324 | "version": "3.0.3", 1325 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1326 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 1327 | }, 1328 | "simple-concat": { 1329 | "version": "1.0.0", 1330 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", 1331 | "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" 1332 | }, 1333 | "simple-get": { 1334 | "version": "3.1.0", 1335 | "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", 1336 | "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", 1337 | "requires": { 1338 | "decompress-response": "^4.2.0", 1339 | "once": "^1.3.1", 1340 | "simple-concat": "^1.0.0" 1341 | } 1342 | }, 1343 | "sntp": { 1344 | "version": "2.1.0", 1345 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", 1346 | "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", 1347 | "requires": { 1348 | "hoek": "4.x.x" 1349 | } 1350 | }, 1351 | "sqlite3": { 1352 | "version": "4.1.1", 1353 | "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz", 1354 | "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==", 1355 | "requires": { 1356 | "nan": "^2.12.1", 1357 | "node-pre-gyp": "^0.11.0", 1358 | "request": "^2.87.0" 1359 | } 1360 | }, 1361 | "sshpk": { 1362 | "version": "1.16.1", 1363 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 1364 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 1365 | "requires": { 1366 | "asn1": "~0.2.3", 1367 | "assert-plus": "^1.0.0", 1368 | "bcrypt-pbkdf": "^1.0.0", 1369 | "dashdash": "^1.12.0", 1370 | "ecc-jsbn": "~0.1.1", 1371 | "getpass": "^0.1.1", 1372 | "jsbn": "~0.1.0", 1373 | "safer-buffer": "^2.0.2", 1374 | "tweetnacl": "~0.14.0" 1375 | } 1376 | }, 1377 | "string-width": { 1378 | "version": "1.0.2", 1379 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1380 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1381 | "requires": { 1382 | "code-point-at": "^1.0.0", 1383 | "is-fullwidth-code-point": "^1.0.0", 1384 | "strip-ansi": "^3.0.0" 1385 | } 1386 | }, 1387 | "string_decoder": { 1388 | "version": "1.1.1", 1389 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1390 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1391 | "requires": { 1392 | "safe-buffer": "~5.1.0" 1393 | } 1394 | }, 1395 | "stringstream": { 1396 | "version": "0.0.6", 1397 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", 1398 | "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==" 1399 | }, 1400 | "strip-ansi": { 1401 | "version": "3.0.1", 1402 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1403 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1404 | "requires": { 1405 | "ansi-regex": "^2.0.0" 1406 | } 1407 | }, 1408 | "strip-json-comments": { 1409 | "version": "2.0.1", 1410 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1411 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1412 | }, 1413 | "tar": { 1414 | "version": "4.4.10", 1415 | "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz", 1416 | "integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==", 1417 | "requires": { 1418 | "chownr": "^1.1.1", 1419 | "fs-minipass": "^1.2.5", 1420 | "minipass": "^2.3.5", 1421 | "minizlib": "^1.2.1", 1422 | "mkdirp": "^0.5.0", 1423 | "safe-buffer": "^5.1.2", 1424 | "yallist": "^3.0.3" 1425 | }, 1426 | "dependencies": { 1427 | "mkdirp": { 1428 | "version": "0.5.5", 1429 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 1430 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 1431 | "requires": { 1432 | "minimist": "^1.2.5" 1433 | } 1434 | } 1435 | } 1436 | }, 1437 | "tar-fs": { 1438 | "version": "2.0.1", 1439 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", 1440 | "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", 1441 | "requires": { 1442 | "chownr": "^1.1.1", 1443 | "mkdirp-classic": "^0.5.2", 1444 | "pump": "^3.0.0", 1445 | "tar-stream": "^2.0.0" 1446 | } 1447 | }, 1448 | "tar-stream": { 1449 | "version": "2.1.2", 1450 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", 1451 | "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", 1452 | "requires": { 1453 | "bl": "^4.0.1", 1454 | "end-of-stream": "^1.4.1", 1455 | "fs-constants": "^1.0.0", 1456 | "inherits": "^2.0.3", 1457 | "readable-stream": "^3.1.1" 1458 | }, 1459 | "dependencies": { 1460 | "readable-stream": { 1461 | "version": "3.6.0", 1462 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1463 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1464 | "requires": { 1465 | "inherits": "^2.0.3", 1466 | "string_decoder": "^1.1.1", 1467 | "util-deprecate": "^1.0.1" 1468 | } 1469 | } 1470 | } 1471 | }, 1472 | "through": { 1473 | "version": "2.3.8", 1474 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1475 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 1476 | }, 1477 | "tldjs": { 1478 | "version": "1.8.0", 1479 | "resolved": "https://registry.npmjs.org/tldjs/-/tldjs-1.8.0.tgz", 1480 | "integrity": "sha1-ucFr1t41e1X/y+fVvkH2s8p24/o=", 1481 | "requires": { 1482 | "punycode": "^1.4.1" 1483 | }, 1484 | "dependencies": { 1485 | "punycode": { 1486 | "version": "1.4.1", 1487 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1488 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1489 | } 1490 | } 1491 | }, 1492 | "tmp": { 1493 | "version": "0.2.0", 1494 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.0.tgz", 1495 | "integrity": "sha512-spsb5g6EiPmteS5TcOAECU3rltCMDMp4VMU2Sb0+WttN4qGobEkMAd+dkr1cubscN08JGNDX765dPbGImbG7MQ==", 1496 | "requires": { 1497 | "rimraf": "^3.0.0" 1498 | }, 1499 | "dependencies": { 1500 | "rimraf": { 1501 | "version": "3.0.2", 1502 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1503 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1504 | "requires": { 1505 | "glob": "^7.1.3" 1506 | } 1507 | } 1508 | } 1509 | }, 1510 | "tough-cookie": { 1511 | "version": "2.5.0", 1512 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 1513 | "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 1514 | "requires": { 1515 | "psl": "^1.1.28", 1516 | "punycode": "^2.1.1" 1517 | } 1518 | }, 1519 | "tunnel-agent": { 1520 | "version": "0.6.0", 1521 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1522 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1523 | "requires": { 1524 | "safe-buffer": "^5.0.1" 1525 | } 1526 | }, 1527 | "tweetnacl": { 1528 | "version": "0.14.5", 1529 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1530 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1531 | }, 1532 | "unbzip2-stream": { 1533 | "version": "1.4.2", 1534 | "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz", 1535 | "integrity": "sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==", 1536 | "requires": { 1537 | "buffer": "^5.2.1", 1538 | "through": "^2.3.8" 1539 | } 1540 | }, 1541 | "uri-js": { 1542 | "version": "4.2.2", 1543 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1544 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1545 | "requires": { 1546 | "punycode": "^2.1.0" 1547 | } 1548 | }, 1549 | "url-status-code": { 1550 | "version": "2.0.0", 1551 | "resolved": "https://registry.npmjs.org/url-status-code/-/url-status-code-2.0.0.tgz", 1552 | "integrity": "sha512-ODpSV5o2OM/tldo6sq5BGl6ILQ0htv+1LSv66qQI94vG2quKMpfxYp2XDGSI0sR9CXZaLJpQ74xMGLiIkQXJzQ==", 1553 | "requires": { 1554 | "valid-url": "1.0.9" 1555 | } 1556 | }, 1557 | "util-deprecate": { 1558 | "version": "1.0.2", 1559 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1560 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1561 | }, 1562 | "uuid": { 1563 | "version": "3.3.3", 1564 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", 1565 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" 1566 | }, 1567 | "valid-url": { 1568 | "version": "1.0.9", 1569 | "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", 1570 | "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA=" 1571 | }, 1572 | "verror": { 1573 | "version": "1.10.0", 1574 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1575 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1576 | "requires": { 1577 | "assert-plus": "^1.0.0", 1578 | "core-util-is": "1.0.2", 1579 | "extsprintf": "^1.2.0" 1580 | } 1581 | }, 1582 | "which-pm-runs": { 1583 | "version": "1.0.0", 1584 | "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", 1585 | "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" 1586 | }, 1587 | "wide-align": { 1588 | "version": "1.1.3", 1589 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1590 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1591 | "requires": { 1592 | "string-width": "^1.0.2 || 2" 1593 | } 1594 | }, 1595 | "win-dpapi": { 1596 | "version": "0.1.0", 1597 | "resolved": "https://registry.npmjs.org/win-dpapi/-/win-dpapi-0.1.0.tgz", 1598 | "integrity": "sha512-sQ1GFRMWd4OAOUBaEMRoQvHHMkaVb0Ib5MhRc4JPWBDytnAiXJhEZTIRt5+D3zPrBmjLyErhrHflmmty2OtxPw==", 1599 | "optional": true, 1600 | "requires": { 1601 | "bindings": "^1.5.0", 1602 | "nan": "^2.13.2" 1603 | } 1604 | }, 1605 | "wrappy": { 1606 | "version": "1.0.2", 1607 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1608 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1609 | }, 1610 | "ws": { 1611 | "version": "7.2.5", 1612 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz", 1613 | "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==" 1614 | }, 1615 | "yallist": { 1616 | "version": "3.1.1", 1617 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1618 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1619 | }, 1620 | "yauzl": { 1621 | "version": "2.10.0", 1622 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", 1623 | "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", 1624 | "requires": { 1625 | "buffer-crc32": "~0.2.3", 1626 | "fd-slicer": "~1.1.0" 1627 | } 1628 | } 1629 | } 1630 | } 1631 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-pinterest-crawl", 3 | "version": "1.3.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 8 | "license": "MIT", 9 | "dependencies": { 10 | "async": "^3.2.0", 11 | "better-sqlite3": "^6.0.1", 12 | "cheerio": "^1.0.0-rc.3", 13 | "chrome-cookies-secure": "^1.3.2", 14 | "dateformat": "^3.0.3", 15 | "env-paths": "^2.2.0", 16 | "image-size": "^0.8.3", 17 | "md5": "^2.2.1", 18 | "mkdirp": "^1.0.4", 19 | "node-wget": "^0.4.3", 20 | "puppeteer": "^3.0.1", 21 | "tmp": "^0.2.0", 22 | "url-status-code": "^2.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /archivist-pinterest-crawl/query/index.js: -------------------------------------------------------------------------------- 1 | const envPaths = require("env-paths"); 2 | const Database = require("better-sqlite3"); 3 | const path = require("path"); 4 | 5 | const DATA_PATH = envPaths("archivist-pinterest").data; 6 | const ASSETS_PATH = path.join(DATA_PATH, "assets"); 7 | 8 | const query = async (_, text) => { 9 | const db = new Database(path.join(DATA_PATH, "data.db")); 10 | let search; 11 | 12 | if (text) { 13 | search = db 14 | .prepare( 15 | ` 16 | SELECT * 17 | FROM data 18 | WHERE 19 | board LIKE :query OR 20 | text LIKE :query OR 21 | title LIKE :query OR 22 | link LIKE :query 23 | ` 24 | ) 25 | .all({ query: `%${text}%` }); 26 | } else { 27 | search = db.prepare("SELECT * FROM data").all(); 28 | } 29 | 30 | return search.map(d => ({ 31 | img: path.join(ASSETS_PATH, d.filename), 32 | link: d.link, 33 | id: d.pinid, 34 | time: d.createdat || d.crawldate, 35 | 36 | width: d.width, 37 | height: d.height, 38 | 39 | meta: { 40 | source: "pinterest", 41 | title: d.title, 42 | note: d.text, 43 | tags: [d.board] 44 | } 45 | })); 46 | }; 47 | 48 | module.exports = query; 49 | -------------------------------------------------------------------------------- /archivist-ui/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /archivist-ui/README.md: -------------------------------------------------------------------------------- 1 | # archivist-ui 2 | 3 | UI for [Archivist](../). 4 | 5 | ## Credits 6 | 7 | Icon by [ovtaviotti](https://www.deviantart.com/octaviotti/art/HiddenMe-789204984) 8 | -------------------------------------------------------------------------------- /archivist-ui/assets/Archivist.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/archivist/2061bc231b03437313f9f2eb41fdaec66aeebbe9/archivist-ui/assets/Archivist.icns -------------------------------------------------------------------------------- /archivist-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "archivist-ui", 3 | "version": "1.1.0", 4 | "description": "ui for archivist-cli", 5 | "scripts": { 6 | "dev": "electron-webpack dev", 7 | "compile": "electron-webpack", 8 | "dist": "npm run compile && electron-builder" 9 | }, 10 | "build": { 11 | "appId": "com.szymonkaliski.archivist-ui", 12 | "productName": "Archivist", 13 | "mac": { 14 | "icon": "./assets/Archivist.icns" 15 | } 16 | }, 17 | "electronWebpack": { 18 | "title": "Archivist", 19 | "whiteListedModules": [ 20 | "use-debounce", 21 | "react-hotkeys-hook" 22 | ] 23 | }, 24 | "keywords": [], 25 | "author": "Szymon Kaliski (http://szymonkaliski.com)", 26 | "license": "MIT", 27 | "devDependencies": { 28 | "@babel/preset-react": "^7.9.4", 29 | "css-loader": "^3.5.3", 30 | "electron": "^8.2.3", 31 | "electron-builder": "^22.5.1", 32 | "electron-webpack": "^2.8.2", 33 | "webpack": "^4.43.0" 34 | }, 35 | "dependencies": { 36 | "dateformat": "^3.0.3", 37 | "fuse.js": "^5.2.3", 38 | "immer": "^6.0.3", 39 | "lodash": "^4.17.15", 40 | "react": "16.13.1", 41 | "react-dom": "16.13.1", 42 | "react-hotkeys-hook": "^2.1.3", 43 | "react-virtualized": "^9.21.2", 44 | "source-map-support": "^0.5.19", 45 | "strip": "^3.0.0", 46 | "tachyons": "^4.11.1", 47 | "use-debounce": "^3.4.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /archivist-ui/scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 4 | cd "$DIR/.." || exit 1 5 | 6 | npm run dist 7 | rm -rf /Applications/Archivist.app 8 | mv ./dist/mac/Archivist.app /Applications 9 | rm -rf ./dist 10 | -------------------------------------------------------------------------------- /archivist-ui/src/main/index.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { app, BrowserWindow } = require("electron"); 3 | const { format } = require("url"); 4 | 5 | const IS_DEV = process.env.NODE_ENV !== "production"; 6 | 7 | let mainWindow; 8 | 9 | const createWindow = () => { 10 | const window = new BrowserWindow({ 11 | webPreferences: { 12 | webSecurity: false, // otherwise we can't load images using file:/// url 13 | nodeIntegration: true 14 | } 15 | }); 16 | 17 | if (IS_DEV) { 18 | window.webContents.openDevTools(); 19 | window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`); 20 | } else { 21 | window.loadURL( 22 | format({ 23 | pathname: path.join(__dirname, "index.html"), 24 | protocol: "file", 25 | slashes: true 26 | }) 27 | ); 28 | } 29 | 30 | window.on("closed", () => { 31 | mainWindow = null; 32 | }); 33 | 34 | return window; 35 | }; 36 | 37 | app.on("window-all-closed", () => { 38 | if (process.platform !== "darwin") { 39 | app.quit(); 40 | } 41 | }); 42 | 43 | app.on("activate", () => { 44 | if (mainWindow === null) { 45 | mainWindow = createWindow(); 46 | } 47 | }); 48 | 49 | app.on("ready", () => { 50 | mainWindow = createWindow(); 51 | }); 52 | -------------------------------------------------------------------------------- /archivist-ui/src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Archivist 8 | 9 | 12 | 13 | 14 |
15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /archivist-ui/src/renderer/index.js: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const ReactDOM = require("react-dom"); 3 | const dateFormat = require("dateformat"); 4 | const strip = require("strip"); 5 | const { chain, identity } = require("lodash"); 6 | const { produce } = require("immer"); 7 | const { shell } = require("electron"); 8 | const { spawn, spawnSync } = require("child_process"); 9 | const { useDebounce } = require("use-debounce"); 10 | const { useHotkeys } = require("react-hotkeys-hook"); 11 | 12 | const { 13 | AutoSizer, 14 | CellMeasurer, 15 | CellMeasurerCache, 16 | createMasonryCellPositioner, 17 | Masonry, 18 | } = require("react-virtualized"); 19 | 20 | require("tachyons/src/tachyons.css"); 21 | require("react-virtualized/styles.css"); 22 | 23 | const { useEffect, useCallback, useRef, useReducer } = React; 24 | 25 | const SPACER = 10; 26 | const SHELL = process.env.SHELL || "bash"; 27 | 28 | // running archivist in an interactive shell to support stuff like nvm 29 | const HAS_ARCHIVIST = !spawnSync(SHELL, ["-i", "-c", "archivist"]).error; 30 | 31 | const executeCLI = async (command, args) => { 32 | return new Promise((resolve, reject) => { 33 | // running archivist in an interactive shell to support stuff like nvm 34 | const cmdArgs = [ 35 | "-i", 36 | "-c", 37 | ["archivist", command, args, "--json"].filter(identity).join(" "), 38 | ]; 39 | 40 | const process = spawn(SHELL, cmdArgs); 41 | let result = ""; 42 | 43 | process.stdout.on("data", (data) => { 44 | result += data.toString(); 45 | }); 46 | 47 | process.stderr.on("data", (data) => { 48 | // we can have stderr AND data at the same time, this shouldn't reject the results completely... 49 | // reject(data); 50 | }); 51 | 52 | process.on("exit", () => { 53 | // sometimes shell leaves control sequences... 54 | const clean = result.replace(/^.*\[/, "["); 55 | resolve(JSON.parse(clean)); 56 | }); 57 | }); 58 | }; 59 | 60 | const calcColumnWidth = ({ width }) => { 61 | return width / Math.floor(width / 400) - SPACER; 62 | }; 63 | 64 | const HoverInfo = ({ meta, link, img, time, setSearchText }) => ( 65 |
69 | link && shell.openExternal(link)} 73 | > 74 | {meta.title || link} 75 | 76 | 77 | {meta.note &&
{strip(meta.note)}
} 78 | 79 | {meta.tags && ( 80 |
81 | {meta.tags.map((tag) => ( 82 | setSearchText(tag)} 87 | > 88 | {tag} 89 | 90 | ))} 91 |
92 | )} 93 | 94 |
95 |
96 | {[ 97 | link && ["src", () => shell.openExternal(link)], 98 | meta.static && ["frozen", () => shell.openItem(meta.static)], 99 | ["img", () => shell.openItem(img)], 100 | ] 101 | .filter(identity) 102 | .map(([text, callback]) => ( 103 | 109 | {text} 110 | 111 | ))} 112 |
113 |
114 | {meta.source} / {dateFormat(time, "yyyy-mm-dd")} 115 |
116 |
117 |
118 | ); 119 | 120 | const createCellRenderer = ({ 121 | data, 122 | width, 123 | cache, 124 | setHoveredId, 125 | hoveredId, 126 | setSearchText, 127 | }) => ({ index, key, parent, style }) => { 128 | const columnWidth = calcColumnWidth({ width }); 129 | const datum = data[index] || {}; 130 | const ratio = datum.height / datum.width; 131 | const imgPath = datum.img; 132 | 133 | return ( 134 | 135 |
setHoveredId(datum.id)} 139 | onMouseLeave={() => setHoveredId(null)} 140 | > 141 |
153 | {hoveredId === datum.id && ( 154 | 155 | )} 156 |
157 |
158 |
159 | ); 160 | }; 161 | 162 | const SearchOverlay = React.forwardRef( 163 | ({ searchText, setSearchText, setIsSearching }, ref) => ( 164 |
168 |
/
169 | setSearchText(e.target.value)} 175 | onKeyDown={(e) => { 176 | // escape 177 | if (e.keyCode === 27) { 178 | setIsSearching(false); 179 | } 180 | }} 181 | /> 182 |
183 | ) 184 | ); 185 | 186 | const reducer = (state, action) => { 187 | if (action.type === "SET_DATA") { 188 | state.data = action.data; 189 | } 190 | 191 | if (action.type === "SET_IS_SEARCHING") { 192 | state.isSearching = action.isSearching; 193 | state.searchText = ""; 194 | } 195 | 196 | if (action.type === "SET_SEARCH_TEXT") { 197 | state.isSearching = true; 198 | state.searchText = action.searchText; 199 | } 200 | 201 | if (action.type === "SET_HOVER_ID") { 202 | state.hoverId = action.hoverId; 203 | } 204 | 205 | return state; 206 | }; 207 | 208 | const immutableReducer = produce(reducer); 209 | 210 | const App = () => { 211 | const [state, dispatch] = useReducer(immutableReducer, { 212 | data: [], 213 | searchText: "", 214 | isSearching: false, 215 | hoverId: null, 216 | }); 217 | 218 | const [debouncedSearchText] = useDebounce(state.searchText, 30); 219 | 220 | const searchInputRef = useRef(null); 221 | const masonry = useRef(null); 222 | 223 | useHotkeys( 224 | "/", 225 | () => { 226 | if (!state.isSearching) { 227 | dispatch({ type: "SET_IS_SEARCHING", isSearching: true }); 228 | } else if (searchInputRef.current) { 229 | searchInputRef.current.focus(); 230 | } 231 | 232 | return false; 233 | }, 234 | [state.isSearching] 235 | ); 236 | 237 | useHotkeys( 238 | "esc", 239 | () => { 240 | if (state.isSearching) { 241 | dispatch({ type: "SET_IS_SEARCHING", isSearching: false }); 242 | } 243 | 244 | return false; 245 | }, 246 | [state.isSearching] 247 | ); 248 | 249 | const cache = useRef( 250 | new CellMeasurerCache({ 251 | defaultHeight: 400, 252 | defaultWidth: 400, 253 | fixedWidth: true, 254 | }) 255 | ); 256 | 257 | const cellPositioner = useRef( 258 | createMasonryCellPositioner({ 259 | cellMeasurerCache: cache.current, 260 | columnCount: 3, 261 | columnWidth: 400, 262 | spacer: SPACER, 263 | }) 264 | ); 265 | 266 | useEffect(() => { 267 | executeCLI("search", debouncedSearchText) 268 | .then((data) => { 269 | const finalData = chain(data) 270 | .map((d) => ({ 271 | ...d, 272 | time: new Date(d.time), 273 | })) 274 | .sortBy((d) => d.time) 275 | .reverse() 276 | .value(); 277 | 278 | dispatch({ type: "SET_DATA", data: finalData }); 279 | }) 280 | .catch((e) => { 281 | console.error("archivist-cli error", e.toString()); 282 | }); 283 | }, [debouncedSearchText]); 284 | 285 | const onResize = useCallback( 286 | ({ width }) => { 287 | const columnWidth = calcColumnWidth({ width }); 288 | const columnCount = Math.floor(Math.max(width / columnWidth, 1)); 289 | 290 | if (cache.current) { 291 | cache.current.clearAll(); 292 | } 293 | 294 | if (cellPositioner.current) { 295 | cellPositioner.current.reset({ 296 | columnCount, 297 | columnWidth, 298 | spacer: SPACER, 299 | }); 300 | } 301 | 302 | if (masonry.current) { 303 | masonry.current.clearCellPositions(); 304 | } 305 | }, 306 | [cache, cellPositioner, masonry] 307 | ); 308 | 309 | if (!HAS_ARCHIVIST) { 310 | return ( 311 |
312 | Error: archivist cli tool not found 313 |
314 | ); 315 | } 316 | 317 | return ( 318 |
319 | 324 | {({ width, height }) => 325 | state.data.length > 0 ? ( 326 | 340 | dispatch({ type: "SET_HOVER_ID", hoverId }), 341 | hoveredId: state.hoverId, 342 | setSearchText: (searchText) => { 343 | dispatch({ type: "SET_SEARCH_TEXT", searchText }); 344 | }, 345 | })} 346 | width={width} 347 | height={height} 348 | /> 349 | ) : ( 350 |
351 | ) 352 | } 353 | 354 | 355 | {state.isSearching && ( 356 | 360 | dispatch({ type: "SET_IS_SEARCHING", isSearching }) 361 | } 362 | setSearchText={(searchText) => 363 | dispatch({ type: "SET_SEARCH_TEXT", searchText }) 364 | } 365 | /> 366 | )} 367 |
368 | ); 369 | }; 370 | 371 | const rootEl = document.getElementById("app"); 372 | ReactDOM.render(, rootEl); 373 | -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szymonkaliski/archivist/2061bc231b03437313f9f2eb41fdaec66aeebbe9/assets/screenshot.png --------------------------------------------------------------------------------