├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib └── grabity.js ├── package-lock.json ├── package.json ├── test └── tests.js ├── test_setup ├── config.js ├── server.js ├── tags.js └── test.html └── utils └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "node" 5 | - "8" 6 | 7 | script: 8 | - npm run-script test-server & 9 | - sleep 5 10 | - npm test 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Emmanuel Olaojo 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 | ## [![Travis](https://img.shields.io/travis/e-oj/grabity.svg?style=flat-square)](https://travis-ci.org/e-oj/grabity) [![npm](https://img.shields.io/npm/l/grabity.svg?style=flat-square)](https://www.npmjs.com/package/grabity) [![npm](https://img.shields.io/npm/v/grabity.svg?style=flat-square)](https://www.npmjs.com/package/grabity) 2 | 3 | # grabity 4 | ## Get preview data from a link. Just grab it! 🎣 5 | 6 | [og]: 7 | [twitter]: 8 | 9 | Grabity looks through [Open Graph](http://ogp.me/) and [Twitter Cards](https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/markup) markup to get Information about a link. Its functions will return as much data as they can from the markup. If no [og] or [twitter] tags are found, it will default to the content of the tag and meta description, and if either the title tag or meta description is missing, the returned property will be empty. 10 | 11 | ## Getting Started: 12 | ``` 13 | npm install grabity 14 | ``` 15 | 16 | ## Usage: 17 | It's really quite simple: 18 | ```javascript 19 | let grabity = require("grabity"); 20 | 21 | (async () => { 22 | let it = await grabity.grabIt("https://github.com/e-oj/grabity"); 23 | 24 | console.log(it); 25 | })(); 26 | ``` 27 | 28 | Should produce: 29 | ``` 30 | { 31 | title: 'e-oj/grabity', 32 | description: 'grabity - Get preview data from a link. Just grab it! 🎣', 33 | image: 'https://avatars0.githubusercontent.com/u/9700116?s=400&v=4', 34 | favicon: 'https://assets-cdn.github.com/favicon.ico' 35 | } 36 | ``` 37 | 38 | ## API 39 | 40 | ### grabity.grabIt(url): Gets a title, description, image, and favicon from a url 41 | > url (required): url to be used 42 | 43 | > returns: object containing title, description, image, and favicon if found 44 | 45 | Gets the [og] or [twitter] title, description and image from a url, as well as the favicon, and returns them in an object. If [og] and [twitter] tags exist for a property, the [og] tag is given preference. The [twitter] tag is selected if an [og] tag does not exist for a property. If there is no tag ([og] or [twitter]) for a property, that property is not included in the returned object. 46 | 47 | ```javascript 48 | let grabity = require("grabity"); 49 | 50 | (async () => { 51 | let it = await grabity.grabIt("https://www.flickr.com"); 52 | 53 | console.log(it); 54 | })(); 55 | ``` 56 | 57 | result: 58 | ``` 59 | { 60 | title: 'Flickr, a Yahoo company', 61 | description: 'Flickr is almost certainly the best online photo management and sharing application in the world. Show off your favorite photos and videos to the world, securely and privately show content to your friends and family, or blog the photos and videos you take with a cameraphone.', 62 | image: 'https://farm4.staticflickr.com/3914/15118079089_489aa62638_b.jpg', 63 | favicon: 'https://s.yimg.com/pw/favicon.ico' 64 | } 65 | ``` 66 | 67 | ### grabity.grab(url): Gets all [og] + [twitter] tags and their values, as well as the favicon, 68 | > url (required): url to be used 69 | 70 | > returns: object containing all found [og] + [twitter] tags and values, and favicon 71 | 72 | Gets all existing [og] and [twitter] tags, as well as the favicon, from the markup and returns them in an object. 73 | 74 | ```javascript 75 | let grabity = require("grabity"); 76 | 77 | (async () => { 78 | let tags = await grabity.grab("https://www.flickr.com"); 79 | 80 | console.log(tags); 81 | })(); 82 | ``` 83 | 84 | result: 85 | ``` 86 | { 87 | 'og:site_name': 'Flickr', 88 | 'og:updated_time': '2017-11-19T21:29:36.577Z', 89 | 'og:title': 'Flickr, a Yahoo company', 90 | 'og:type': 'website', 91 | 'og:description': 'Flickr is almost certainly the best online photo management and sharing application in the world. Show off your favorite photos and videos to the world, securely and privately show content to your friends and family, or blog the photos and videos you take with a cameraphone.', 92 | 'og:image': 'https://farm4.staticflickr.com/3914/15118079089_489aa62638_b.jpg', 93 | 'twitter:card': 'summary_large_image', 94 | 'twitter:creator': '@flickr', 95 | 'twitter:title': 'Flickr, a Yahoo company', 96 | 'twitter:description': 'Flickr is almost certainly the best online photo management and sharing application in the world. Show off your favorite photos and videos to the world, securely and privately show content to your friends and family, or blog the photos and videos you take with a cameraphone.', 97 | 'twitter:image:src': 'https://farm4.staticflickr.com/3914/15118079089_489aa62638_b.jpg', 98 | favicon: 'https://s.yimg.com/pw/favicon.ico' 99 | } 100 | 101 | ``` 102 | <br> 103 | 104 | ## Test 105 | To test this module, cd to the project directory and run: 106 | ``` 107 | npm run-script test-server 108 | ``` 109 | 110 | then in a seperate terminal: 111 | ``` 112 | npm test 113 | ``` 114 | *Note: The test server runs on port 9973 by default. You can change the port number in test_setup/config.js* 115 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/16/17 4 | */ 5 | 6 | module.exports = require("./lib/grabity"); 7 | -------------------------------------------------------------------------------- /lib/grabity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/17/17 4 | */ 5 | 6 | let utils = require("../utils/index"); 7 | 8 | /** 9 | * Gets a title, description and image from 10 | * a url that implements Open Graph or Twitter 11 | * Cards markup. 12 | * 13 | * @param url the url (where "it" lives) 14 | * 15 | * @returns {Promise.<{}>} 16 | */ 17 | exports.grabIt = async (url) => { 18 | if(!url) throw new Error("Missing url!"); 19 | 20 | try{ 21 | let {og, twitter, favicon, defaults} = await utils.grabInfo(url); 22 | let props = ["title", "description", "image"]; 23 | let res = {}; 24 | let val; 25 | 26 | for(let prop of props){ 27 | val = og[prop] || twitter[prop] || defaults[prop]; 28 | 29 | if(val) res[prop] = val; 30 | } 31 | 32 | if (favicon) { 33 | res.favicon = favicon; 34 | } 35 | 36 | return res; 37 | } 38 | catch (err){ 39 | throw err; 40 | } 41 | }; 42 | 43 | /** 44 | * Gets all Open Graph and Twitter Card 45 | * properties from a url 46 | * 47 | * @param url the url ("it" is here) 48 | * 49 | * @returns {Promise.<*>} 50 | */ 51 | exports.grab = async (url) => { 52 | if(!url) throw new Error("Missing url!"); 53 | 54 | try{ 55 | return await utils.grabAll(url); 56 | } 57 | catch(err){ 58 | throw err; 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grabity", 3 | "version": "1.0.5", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "8.0.53", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.53.tgz", 10 | "integrity": "sha512-54Dm6NwYeiSQmRB1BLXKr5GELi0wFapR1npi8bnZhEcu84d/yQKqnwwXQ56hZ0RUbTG6L5nqDZaN3dgByQXQRQ==" 11 | }, 12 | "abab": { 13 | "version": "1.0.4", 14 | "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", 15 | "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=" 16 | }, 17 | "accepts": { 18 | "version": "1.3.4", 19 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 20 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 21 | "dev": true, 22 | "requires": { 23 | "mime-types": "~2.1.16", 24 | "negotiator": "0.6.1" 25 | } 26 | }, 27 | "acorn": { 28 | "version": "5.2.1", 29 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", 30 | "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==" 31 | }, 32 | "acorn-globals": { 33 | "version": "4.1.0", 34 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", 35 | "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", 36 | "requires": { 37 | "acorn": "^5.0.0" 38 | } 39 | }, 40 | "ajv": { 41 | "version": "5.3.0", 42 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", 43 | "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=", 44 | "requires": { 45 | "co": "^4.6.0", 46 | "fast-deep-equal": "^1.0.0", 47 | "fast-json-stable-stringify": "^2.0.0", 48 | "json-schema-traverse": "^0.3.0" 49 | } 50 | }, 51 | "array-equal": { 52 | "version": "1.0.0", 53 | "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", 54 | "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" 55 | }, 56 | "array-flatten": { 57 | "version": "1.1.1", 58 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 59 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", 60 | "dev": true 61 | }, 62 | "asn1": { 63 | "version": "0.2.3", 64 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 65 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 66 | }, 67 | "assert-plus": { 68 | "version": "1.0.0", 69 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 70 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 71 | }, 72 | "assertion-error": { 73 | "version": "1.0.2", 74 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 75 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", 76 | "dev": true 77 | }, 78 | "asynckit": { 79 | "version": "0.4.0", 80 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 81 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 82 | }, 83 | "aws-sign2": { 84 | "version": "0.7.0", 85 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 86 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 87 | }, 88 | "aws4": { 89 | "version": "1.6.0", 90 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", 91 | "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" 92 | }, 93 | "balanced-match": { 94 | "version": "1.0.0", 95 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 96 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 97 | "dev": true 98 | }, 99 | "basic-auth": { 100 | "version": "2.0.0", 101 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", 102 | "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", 103 | "dev": true, 104 | "requires": { 105 | "safe-buffer": "5.1.1" 106 | } 107 | }, 108 | "bcrypt-pbkdf": { 109 | "version": "1.0.1", 110 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", 111 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", 112 | "optional": true, 113 | "requires": { 114 | "tweetnacl": "^0.14.3" 115 | } 116 | }, 117 | "body-parser": { 118 | "version": "1.18.2", 119 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 120 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 121 | "dev": true, 122 | "requires": { 123 | "bytes": "3.0.0", 124 | "content-type": "~1.0.4", 125 | "debug": "2.6.9", 126 | "depd": "~1.1.1", 127 | "http-errors": "~1.6.2", 128 | "iconv-lite": "0.4.19", 129 | "on-finished": "~2.3.0", 130 | "qs": "6.5.1", 131 | "raw-body": "2.3.2", 132 | "type-is": "~1.6.15" 133 | } 134 | }, 135 | "boom": { 136 | "version": "4.3.1", 137 | "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", 138 | "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", 139 | "requires": { 140 | "hoek": "4.x.x" 141 | } 142 | }, 143 | "brace-expansion": { 144 | "version": "1.1.8", 145 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 146 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 147 | "dev": true, 148 | "requires": { 149 | "balanced-match": "^1.0.0", 150 | "concat-map": "0.0.1" 151 | } 152 | }, 153 | "browser-stdout": { 154 | "version": "1.3.0", 155 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 156 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 157 | "dev": true 158 | }, 159 | "bytes": { 160 | "version": "3.0.0", 161 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 162 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", 163 | "dev": true 164 | }, 165 | "caseless": { 166 | "version": "0.12.0", 167 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 168 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 169 | }, 170 | "chai": { 171 | "version": "4.1.2", 172 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 173 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 174 | "dev": true, 175 | "requires": { 176 | "assertion-error": "^1.0.1", 177 | "check-error": "^1.0.1", 178 | "deep-eql": "^3.0.0", 179 | "get-func-name": "^2.0.0", 180 | "pathval": "^1.0.0", 181 | "type-detect": "^4.0.0" 182 | } 183 | }, 184 | "check-error": { 185 | "version": "1.0.2", 186 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 187 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 188 | "dev": true 189 | }, 190 | "co": { 191 | "version": "4.6.0", 192 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 193 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 194 | }, 195 | "combined-stream": { 196 | "version": "1.0.5", 197 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 198 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 199 | "requires": { 200 | "delayed-stream": "~1.0.0" 201 | } 202 | }, 203 | "commander": { 204 | "version": "2.11.0", 205 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 206 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 207 | "dev": true 208 | }, 209 | "concat-map": { 210 | "version": "0.0.1", 211 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 212 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 213 | "dev": true 214 | }, 215 | "content-disposition": { 216 | "version": "0.5.2", 217 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 218 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", 219 | "dev": true 220 | }, 221 | "content-type": { 222 | "version": "1.0.4", 223 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 224 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 225 | "dev": true 226 | }, 227 | "content-type-parser": { 228 | "version": "1.0.2", 229 | "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", 230 | "integrity": "sha512-lM4l4CnMEwOLHAHr/P6MEZwZFPJFtAAKgL6pogbXmVZggIqXhdB6RbBtPOTsw2FcXwYhehRGERJmRrjOiIB8pQ==" 231 | }, 232 | "cookie": { 233 | "version": "0.3.1", 234 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 235 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", 236 | "dev": true 237 | }, 238 | "cookie-signature": { 239 | "version": "1.0.6", 240 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 241 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", 242 | "dev": true 243 | }, 244 | "core-util-is": { 245 | "version": "1.0.2", 246 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 247 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 248 | }, 249 | "cryptiles": { 250 | "version": "3.1.2", 251 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", 252 | "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", 253 | "requires": { 254 | "boom": "5.x.x" 255 | }, 256 | "dependencies": { 257 | "boom": { 258 | "version": "5.2.0", 259 | "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", 260 | "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", 261 | "requires": { 262 | "hoek": "4.x.x" 263 | } 264 | } 265 | } 266 | }, 267 | "cssom": { 268 | "version": "0.3.2", 269 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", 270 | "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=" 271 | }, 272 | "cssstyle": { 273 | "version": "0.2.37", 274 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", 275 | "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", 276 | "requires": { 277 | "cssom": "0.3.x" 278 | } 279 | }, 280 | "dashdash": { 281 | "version": "1.14.1", 282 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 283 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 284 | "requires": { 285 | "assert-plus": "^1.0.0" 286 | } 287 | }, 288 | "debug": { 289 | "version": "2.6.9", 290 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 291 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 292 | "dev": true, 293 | "requires": { 294 | "ms": "2.0.0" 295 | } 296 | }, 297 | "deep-eql": { 298 | "version": "3.0.1", 299 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 300 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 301 | "dev": true, 302 | "requires": { 303 | "type-detect": "^4.0.0" 304 | } 305 | }, 306 | "deep-is": { 307 | "version": "0.1.3", 308 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 309 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" 310 | }, 311 | "delayed-stream": { 312 | "version": "1.0.0", 313 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 314 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 315 | }, 316 | "depd": { 317 | "version": "1.1.1", 318 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 319 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", 320 | "dev": true 321 | }, 322 | "destroy": { 323 | "version": "1.0.4", 324 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 325 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 326 | "dev": true 327 | }, 328 | "diff": { 329 | "version": "3.3.1", 330 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 331 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", 332 | "dev": true 333 | }, 334 | "domexception": { 335 | "version": "1.0.0", 336 | "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.0.tgz", 337 | "integrity": "sha512-WpwuBlZ2lQRFa4H/4w49deb9rJLot9KmqrKKjMc9qBl7CID+DdC2swoa34ccRl+anL2B6bLp6TjFdIdnzekMBQ==" 338 | }, 339 | "ecc-jsbn": { 340 | "version": "0.1.1", 341 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 342 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 343 | "optional": true, 344 | "requires": { 345 | "jsbn": "~0.1.0" 346 | } 347 | }, 348 | "ee-first": { 349 | "version": "1.1.1", 350 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 351 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 352 | "dev": true 353 | }, 354 | "encodeurl": { 355 | "version": "1.0.1", 356 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", 357 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", 358 | "dev": true 359 | }, 360 | "escape-html": { 361 | "version": "1.0.3", 362 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 363 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 364 | "dev": true 365 | }, 366 | "escape-string-regexp": { 367 | "version": "1.0.5", 368 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 369 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 370 | "dev": true 371 | }, 372 | "escodegen": { 373 | "version": "1.9.0", 374 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz", 375 | "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", 376 | "requires": { 377 | "esprima": "^3.1.3", 378 | "estraverse": "^4.2.0", 379 | "esutils": "^2.0.2", 380 | "optionator": "^0.8.1", 381 | "source-map": "~0.5.6" 382 | } 383 | }, 384 | "esprima": { 385 | "version": "3.1.3", 386 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", 387 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" 388 | }, 389 | "estraverse": { 390 | "version": "4.2.0", 391 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 392 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" 393 | }, 394 | "esutils": { 395 | "version": "2.0.2", 396 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 397 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" 398 | }, 399 | "etag": { 400 | "version": "1.8.1", 401 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 402 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 403 | "dev": true 404 | }, 405 | "express": { 406 | "version": "4.16.2", 407 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", 408 | "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", 409 | "dev": true, 410 | "requires": { 411 | "accepts": "~1.3.4", 412 | "array-flatten": "1.1.1", 413 | "body-parser": "1.18.2", 414 | "content-disposition": "0.5.2", 415 | "content-type": "~1.0.4", 416 | "cookie": "0.3.1", 417 | "cookie-signature": "1.0.6", 418 | "debug": "2.6.9", 419 | "depd": "~1.1.1", 420 | "encodeurl": "~1.0.1", 421 | "escape-html": "~1.0.3", 422 | "etag": "~1.8.1", 423 | "finalhandler": "1.1.0", 424 | "fresh": "0.5.2", 425 | "merge-descriptors": "1.0.1", 426 | "methods": "~1.1.2", 427 | "on-finished": "~2.3.0", 428 | "parseurl": "~1.3.2", 429 | "path-to-regexp": "0.1.7", 430 | "proxy-addr": "~2.0.2", 431 | "qs": "6.5.1", 432 | "range-parser": "~1.2.0", 433 | "safe-buffer": "5.1.1", 434 | "send": "0.16.1", 435 | "serve-static": "1.13.1", 436 | "setprototypeof": "1.1.0", 437 | "statuses": "~1.3.1", 438 | "type-is": "~1.6.15", 439 | "utils-merge": "1.0.1", 440 | "vary": "~1.1.2" 441 | } 442 | }, 443 | "extend": { 444 | "version": "3.0.1", 445 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 446 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 447 | }, 448 | "extsprintf": { 449 | "version": "1.3.0", 450 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 451 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 452 | }, 453 | "fast-deep-equal": { 454 | "version": "1.0.0", 455 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", 456 | "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" 457 | }, 458 | "fast-json-stable-stringify": { 459 | "version": "2.0.0", 460 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 461 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 462 | }, 463 | "fast-levenshtein": { 464 | "version": "2.0.6", 465 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 466 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" 467 | }, 468 | "finalhandler": { 469 | "version": "1.1.0", 470 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", 471 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", 472 | "dev": true, 473 | "requires": { 474 | "debug": "2.6.9", 475 | "encodeurl": "~1.0.1", 476 | "escape-html": "~1.0.3", 477 | "on-finished": "~2.3.0", 478 | "parseurl": "~1.3.2", 479 | "statuses": "~1.3.1", 480 | "unpipe": "~1.0.0" 481 | } 482 | }, 483 | "forever-agent": { 484 | "version": "0.6.1", 485 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 486 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 487 | }, 488 | "form-data": { 489 | "version": "2.3.1", 490 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", 491 | "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", 492 | "requires": { 493 | "asynckit": "^0.4.0", 494 | "combined-stream": "^1.0.5", 495 | "mime-types": "^2.1.12" 496 | } 497 | }, 498 | "forwarded": { 499 | "version": "0.1.2", 500 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 501 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", 502 | "dev": true 503 | }, 504 | "fresh": { 505 | "version": "0.5.2", 506 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 507 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 508 | "dev": true 509 | }, 510 | "fs.realpath": { 511 | "version": "1.0.0", 512 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 513 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 514 | "dev": true 515 | }, 516 | "get-func-name": { 517 | "version": "2.0.0", 518 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 519 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 520 | "dev": true 521 | }, 522 | "getpass": { 523 | "version": "0.1.7", 524 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 525 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 526 | "requires": { 527 | "assert-plus": "^1.0.0" 528 | } 529 | }, 530 | "glob": { 531 | "version": "7.1.2", 532 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 533 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 534 | "dev": true, 535 | "requires": { 536 | "fs.realpath": "^1.0.0", 537 | "inflight": "^1.0.4", 538 | "inherits": "2", 539 | "minimatch": "^3.0.4", 540 | "once": "^1.3.0", 541 | "path-is-absolute": "^1.0.0" 542 | } 543 | }, 544 | "growl": { 545 | "version": "1.10.3", 546 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 547 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", 548 | "dev": true 549 | }, 550 | "har-schema": { 551 | "version": "2.0.0", 552 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 553 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 554 | }, 555 | "har-validator": { 556 | "version": "5.0.3", 557 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", 558 | "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", 559 | "requires": { 560 | "ajv": "^5.1.0", 561 | "har-schema": "^2.0.0" 562 | } 563 | }, 564 | "has-flag": { 565 | "version": "2.0.0", 566 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 567 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 568 | "dev": true 569 | }, 570 | "hawk": { 571 | "version": "6.0.2", 572 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", 573 | "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", 574 | "requires": { 575 | "boom": "4.x.x", 576 | "cryptiles": "3.x.x", 577 | "hoek": "4.x.x", 578 | "sntp": "2.x.x" 579 | } 580 | }, 581 | "he": { 582 | "version": "1.1.1", 583 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 584 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 585 | "dev": true 586 | }, 587 | "hoek": { 588 | "version": "4.2.0", 589 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", 590 | "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" 591 | }, 592 | "html-encoding-sniffer": { 593 | "version": "1.0.2", 594 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", 595 | "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", 596 | "requires": { 597 | "whatwg-encoding": "^1.0.1" 598 | } 599 | }, 600 | "http-errors": { 601 | "version": "1.6.2", 602 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 603 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 604 | "dev": true, 605 | "requires": { 606 | "depd": "1.1.1", 607 | "inherits": "2.0.3", 608 | "setprototypeof": "1.0.3", 609 | "statuses": ">= 1.3.1 < 2" 610 | }, 611 | "dependencies": { 612 | "setprototypeof": { 613 | "version": "1.0.3", 614 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 615 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", 616 | "dev": true 617 | } 618 | } 619 | }, 620 | "http-signature": { 621 | "version": "1.2.0", 622 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 623 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 624 | "requires": { 625 | "assert-plus": "^1.0.0", 626 | "jsprim": "^1.2.2", 627 | "sshpk": "^1.7.0" 628 | } 629 | }, 630 | "iconv-lite": { 631 | "version": "0.4.19", 632 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 633 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 634 | }, 635 | "inflight": { 636 | "version": "1.0.6", 637 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 638 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 639 | "dev": true, 640 | "requires": { 641 | "once": "^1.3.0", 642 | "wrappy": "1" 643 | } 644 | }, 645 | "inherits": { 646 | "version": "2.0.3", 647 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 648 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 649 | "dev": true 650 | }, 651 | "ipaddr.js": { 652 | "version": "1.5.2", 653 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", 654 | "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=", 655 | "dev": true 656 | }, 657 | "is-typedarray": { 658 | "version": "1.0.0", 659 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 660 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 661 | }, 662 | "isstream": { 663 | "version": "0.1.2", 664 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 665 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 666 | }, 667 | "jsbn": { 668 | "version": "0.1.1", 669 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 670 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 671 | "optional": true 672 | }, 673 | "jsdom": { 674 | "version": "11.3.0", 675 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.3.0.tgz", 676 | "integrity": "sha512-aPZTDl4MplzQhx5bLztk6nzjbEslmO3Q3+z0WpCMutL1XJDhZIRzir6R1Y8S84LgeT/7jhQvgtUMkY6oPwvlUw==", 677 | "requires": { 678 | "abab": "^1.0.3", 679 | "acorn": "^5.1.2", 680 | "acorn-globals": "^4.0.0", 681 | "array-equal": "^1.0.0", 682 | "content-type-parser": "^1.0.1", 683 | "cssom": ">= 0.3.2 < 0.4.0", 684 | "cssstyle": ">= 0.2.37 < 0.3.0", 685 | "domexception": "^1.0.0", 686 | "escodegen": "^1.9.0", 687 | "html-encoding-sniffer": "^1.0.1", 688 | "nwmatcher": "^1.4.1", 689 | "parse5": "^3.0.2", 690 | "pn": "^1.0.0", 691 | "request": "^2.83.0", 692 | "request-promise-native": "^1.0.3", 693 | "sax": "^1.2.1", 694 | "symbol-tree": "^3.2.1", 695 | "tough-cookie": "^2.3.3", 696 | "webidl-conversions": "^4.0.2", 697 | "whatwg-encoding": "^1.0.1", 698 | "whatwg-url": "^6.3.0", 699 | "xml-name-validator": "^2.0.1" 700 | } 701 | }, 702 | "json-schema": { 703 | "version": "0.2.3", 704 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 705 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 706 | }, 707 | "json-schema-traverse": { 708 | "version": "0.3.1", 709 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 710 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 711 | }, 712 | "json-stringify-safe": { 713 | "version": "5.0.1", 714 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 715 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 716 | }, 717 | "jsprim": { 718 | "version": "1.4.1", 719 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 720 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 721 | "requires": { 722 | "assert-plus": "1.0.0", 723 | "extsprintf": "1.3.0", 724 | "json-schema": "0.2.3", 725 | "verror": "1.10.0" 726 | } 727 | }, 728 | "levn": { 729 | "version": "0.3.0", 730 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 731 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 732 | "requires": { 733 | "prelude-ls": "~1.1.2", 734 | "type-check": "~0.3.2" 735 | } 736 | }, 737 | "lodash": { 738 | "version": "4.17.4", 739 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 740 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 741 | }, 742 | "lodash.sortby": { 743 | "version": "4.7.0", 744 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 745 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" 746 | }, 747 | "media-typer": { 748 | "version": "0.3.0", 749 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 750 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 751 | "dev": true 752 | }, 753 | "merge-descriptors": { 754 | "version": "1.0.1", 755 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 756 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", 757 | "dev": true 758 | }, 759 | "methods": { 760 | "version": "1.1.2", 761 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 762 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 763 | "dev": true 764 | }, 765 | "mime": { 766 | "version": "1.4.1", 767 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 768 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", 769 | "dev": true 770 | }, 771 | "mime-db": { 772 | "version": "1.30.0", 773 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 774 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 775 | }, 776 | "mime-types": { 777 | "version": "2.1.17", 778 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 779 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 780 | "requires": { 781 | "mime-db": "~1.30.0" 782 | } 783 | }, 784 | "minimatch": { 785 | "version": "3.0.4", 786 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 787 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 788 | "dev": true, 789 | "requires": { 790 | "brace-expansion": "^1.1.7" 791 | } 792 | }, 793 | "minimist": { 794 | "version": "0.0.8", 795 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 796 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 797 | "dev": true 798 | }, 799 | "mkdirp": { 800 | "version": "0.5.1", 801 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 802 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 803 | "dev": true, 804 | "requires": { 805 | "minimist": "0.0.8" 806 | } 807 | }, 808 | "mocha": { 809 | "version": "4.0.1", 810 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", 811 | "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", 812 | "dev": true, 813 | "requires": { 814 | "browser-stdout": "1.3.0", 815 | "commander": "2.11.0", 816 | "debug": "3.1.0", 817 | "diff": "3.3.1", 818 | "escape-string-regexp": "1.0.5", 819 | "glob": "7.1.2", 820 | "growl": "1.10.3", 821 | "he": "1.1.1", 822 | "mkdirp": "0.5.1", 823 | "supports-color": "4.4.0" 824 | }, 825 | "dependencies": { 826 | "debug": { 827 | "version": "3.1.0", 828 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 829 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 830 | "dev": true, 831 | "requires": { 832 | "ms": "2.0.0" 833 | } 834 | } 835 | } 836 | }, 837 | "morgan": { 838 | "version": "1.9.0", 839 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", 840 | "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", 841 | "dev": true, 842 | "requires": { 843 | "basic-auth": "~2.0.0", 844 | "debug": "2.6.9", 845 | "depd": "~1.1.1", 846 | "on-finished": "~2.3.0", 847 | "on-headers": "~1.0.1" 848 | } 849 | }, 850 | "ms": { 851 | "version": "2.0.0", 852 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 853 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 854 | "dev": true 855 | }, 856 | "negotiator": { 857 | "version": "0.6.1", 858 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 859 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", 860 | "dev": true 861 | }, 862 | "nwmatcher": { 863 | "version": "1.4.3", 864 | "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", 865 | "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==" 866 | }, 867 | "oauth-sign": { 868 | "version": "0.8.2", 869 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 870 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 871 | }, 872 | "on-finished": { 873 | "version": "2.3.0", 874 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 875 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 876 | "dev": true, 877 | "requires": { 878 | "ee-first": "1.1.1" 879 | } 880 | }, 881 | "on-headers": { 882 | "version": "1.0.1", 883 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", 884 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", 885 | "dev": true 886 | }, 887 | "once": { 888 | "version": "1.4.0", 889 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 890 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 891 | "dev": true, 892 | "requires": { 893 | "wrappy": "1" 894 | } 895 | }, 896 | "optionator": { 897 | "version": "0.8.2", 898 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 899 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 900 | "requires": { 901 | "deep-is": "~0.1.3", 902 | "fast-levenshtein": "~2.0.4", 903 | "levn": "~0.3.0", 904 | "prelude-ls": "~1.1.2", 905 | "type-check": "~0.3.2", 906 | "wordwrap": "~1.0.0" 907 | } 908 | }, 909 | "parse5": { 910 | "version": "3.0.3", 911 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", 912 | "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", 913 | "requires": { 914 | "@types/node": "*" 915 | } 916 | }, 917 | "parseurl": { 918 | "version": "1.3.2", 919 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 920 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", 921 | "dev": true 922 | }, 923 | "path-is-absolute": { 924 | "version": "1.0.1", 925 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 926 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 927 | "dev": true 928 | }, 929 | "path-to-regexp": { 930 | "version": "0.1.7", 931 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 932 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", 933 | "dev": true 934 | }, 935 | "pathval": { 936 | "version": "1.1.0", 937 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 938 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 939 | "dev": true 940 | }, 941 | "performance-now": { 942 | "version": "2.1.0", 943 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 944 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 945 | }, 946 | "pn": { 947 | "version": "1.0.0", 948 | "resolved": "https://registry.npmjs.org/pn/-/pn-1.0.0.tgz", 949 | "integrity": "sha1-HPWjCw2AbNGPiPxBprXUrWFbO6k=" 950 | }, 951 | "prelude-ls": { 952 | "version": "1.1.2", 953 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 954 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" 955 | }, 956 | "proxy-addr": { 957 | "version": "2.0.2", 958 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", 959 | "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", 960 | "dev": true, 961 | "requires": { 962 | "forwarded": "~0.1.2", 963 | "ipaddr.js": "1.5.2" 964 | } 965 | }, 966 | "punycode": { 967 | "version": "1.4.1", 968 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 969 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 970 | }, 971 | "qs": { 972 | "version": "6.5.1", 973 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 974 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 975 | }, 976 | "range-parser": { 977 | "version": "1.2.0", 978 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 979 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", 980 | "dev": true 981 | }, 982 | "raw-body": { 983 | "version": "2.3.2", 984 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 985 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 986 | "dev": true, 987 | "requires": { 988 | "bytes": "3.0.0", 989 | "http-errors": "1.6.2", 990 | "iconv-lite": "0.4.19", 991 | "unpipe": "1.0.0" 992 | } 993 | }, 994 | "request": { 995 | "version": "2.83.0", 996 | "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", 997 | "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", 998 | "requires": { 999 | "aws-sign2": "~0.7.0", 1000 | "aws4": "^1.6.0", 1001 | "caseless": "~0.12.0", 1002 | "combined-stream": "~1.0.5", 1003 | "extend": "~3.0.1", 1004 | "forever-agent": "~0.6.1", 1005 | "form-data": "~2.3.1", 1006 | "har-validator": "~5.0.3", 1007 | "hawk": "~6.0.2", 1008 | "http-signature": "~1.2.0", 1009 | "is-typedarray": "~1.0.0", 1010 | "isstream": "~0.1.2", 1011 | "json-stringify-safe": "~5.0.1", 1012 | "mime-types": "~2.1.17", 1013 | "oauth-sign": "~0.8.2", 1014 | "performance-now": "^2.1.0", 1015 | "qs": "~6.5.1", 1016 | "safe-buffer": "^5.1.1", 1017 | "stringstream": "~0.0.5", 1018 | "tough-cookie": "~2.3.3", 1019 | "tunnel-agent": "^0.6.0", 1020 | "uuid": "^3.1.0" 1021 | } 1022 | }, 1023 | "request-promise-core": { 1024 | "version": "1.1.1", 1025 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 1026 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 1027 | "requires": { 1028 | "lodash": "^4.13.1" 1029 | } 1030 | }, 1031 | "request-promise-native": { 1032 | "version": "1.0.5", 1033 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", 1034 | "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", 1035 | "requires": { 1036 | "request-promise-core": "1.1.1", 1037 | "stealthy-require": "^1.1.0", 1038 | "tough-cookie": ">=2.3.3" 1039 | } 1040 | }, 1041 | "safe-buffer": { 1042 | "version": "5.1.1", 1043 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 1044 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 1045 | }, 1046 | "sax": { 1047 | "version": "1.2.4", 1048 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 1049 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 1050 | }, 1051 | "send": { 1052 | "version": "0.16.1", 1053 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", 1054 | "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", 1055 | "dev": true, 1056 | "requires": { 1057 | "debug": "2.6.9", 1058 | "depd": "~1.1.1", 1059 | "destroy": "~1.0.4", 1060 | "encodeurl": "~1.0.1", 1061 | "escape-html": "~1.0.3", 1062 | "etag": "~1.8.1", 1063 | "fresh": "0.5.2", 1064 | "http-errors": "~1.6.2", 1065 | "mime": "1.4.1", 1066 | "ms": "2.0.0", 1067 | "on-finished": "~2.3.0", 1068 | "range-parser": "~1.2.0", 1069 | "statuses": "~1.3.1" 1070 | } 1071 | }, 1072 | "serve-static": { 1073 | "version": "1.13.1", 1074 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", 1075 | "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", 1076 | "dev": true, 1077 | "requires": { 1078 | "encodeurl": "~1.0.1", 1079 | "escape-html": "~1.0.3", 1080 | "parseurl": "~1.3.2", 1081 | "send": "0.16.1" 1082 | } 1083 | }, 1084 | "setprototypeof": { 1085 | "version": "1.1.0", 1086 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1087 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", 1088 | "dev": true 1089 | }, 1090 | "sntp": { 1091 | "version": "2.1.0", 1092 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", 1093 | "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", 1094 | "requires": { 1095 | "hoek": "4.x.x" 1096 | } 1097 | }, 1098 | "source-map": { 1099 | "version": "0.5.7", 1100 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1101 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1102 | "optional": true 1103 | }, 1104 | "sshpk": { 1105 | "version": "1.13.1", 1106 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", 1107 | "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", 1108 | "requires": { 1109 | "asn1": "~0.2.3", 1110 | "assert-plus": "^1.0.0", 1111 | "bcrypt-pbkdf": "^1.0.0", 1112 | "dashdash": "^1.12.0", 1113 | "ecc-jsbn": "~0.1.1", 1114 | "getpass": "^0.1.1", 1115 | "jsbn": "~0.1.0", 1116 | "tweetnacl": "~0.14.0" 1117 | } 1118 | }, 1119 | "statuses": { 1120 | "version": "1.3.1", 1121 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 1122 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", 1123 | "dev": true 1124 | }, 1125 | "stealthy-require": { 1126 | "version": "1.1.1", 1127 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 1128 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" 1129 | }, 1130 | "stringstream": { 1131 | "version": "0.0.5", 1132 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", 1133 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" 1134 | }, 1135 | "supports-color": { 1136 | "version": "4.4.0", 1137 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 1138 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 1139 | "dev": true, 1140 | "requires": { 1141 | "has-flag": "^2.0.0" 1142 | } 1143 | }, 1144 | "symbol-tree": { 1145 | "version": "3.2.2", 1146 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", 1147 | "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" 1148 | }, 1149 | "tough-cookie": { 1150 | "version": "2.3.3", 1151 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", 1152 | "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", 1153 | "requires": { 1154 | "punycode": "^1.4.1" 1155 | } 1156 | }, 1157 | "tr46": { 1158 | "version": "1.0.1", 1159 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", 1160 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", 1161 | "requires": { 1162 | "punycode": "^2.1.0" 1163 | }, 1164 | "dependencies": { 1165 | "punycode": { 1166 | "version": "2.1.0", 1167 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", 1168 | "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" 1169 | } 1170 | } 1171 | }, 1172 | "tunnel-agent": { 1173 | "version": "0.6.0", 1174 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1175 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1176 | "requires": { 1177 | "safe-buffer": "^5.0.1" 1178 | } 1179 | }, 1180 | "tweetnacl": { 1181 | "version": "0.14.5", 1182 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1183 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 1184 | "optional": true 1185 | }, 1186 | "type-check": { 1187 | "version": "0.3.2", 1188 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1189 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1190 | "requires": { 1191 | "prelude-ls": "~1.1.2" 1192 | } 1193 | }, 1194 | "type-detect": { 1195 | "version": "4.0.5", 1196 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", 1197 | "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==", 1198 | "dev": true 1199 | }, 1200 | "type-is": { 1201 | "version": "1.6.15", 1202 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 1203 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 1204 | "dev": true, 1205 | "requires": { 1206 | "media-typer": "0.3.0", 1207 | "mime-types": "~2.1.15" 1208 | } 1209 | }, 1210 | "unpipe": { 1211 | "version": "1.0.0", 1212 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1213 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 1214 | "dev": true 1215 | }, 1216 | "utils-merge": { 1217 | "version": "1.0.1", 1218 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1219 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 1220 | "dev": true 1221 | }, 1222 | "uuid": { 1223 | "version": "3.1.0", 1224 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", 1225 | "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" 1226 | }, 1227 | "vary": { 1228 | "version": "1.1.2", 1229 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1230 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 1231 | "dev": true 1232 | }, 1233 | "verror": { 1234 | "version": "1.10.0", 1235 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1236 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1237 | "requires": { 1238 | "assert-plus": "^1.0.0", 1239 | "core-util-is": "1.0.2", 1240 | "extsprintf": "^1.2.0" 1241 | } 1242 | }, 1243 | "webidl-conversions": { 1244 | "version": "4.0.2", 1245 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", 1246 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" 1247 | }, 1248 | "whatwg-encoding": { 1249 | "version": "1.0.3", 1250 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", 1251 | "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", 1252 | "requires": { 1253 | "iconv-lite": "0.4.19" 1254 | } 1255 | }, 1256 | "whatwg-url": { 1257 | "version": "6.3.0", 1258 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.3.0.tgz", 1259 | "integrity": "sha512-rM+hE5iYKGPAOu05mIdJR47pYSR2vDzfrTEFRc/S8D3L60yW8BuXmUJ7Kog7x/DrokFN7JNaHKadpzjouKRRAw==", 1260 | "requires": { 1261 | "lodash.sortby": "^4.7.0", 1262 | "tr46": "^1.0.0", 1263 | "webidl-conversions": "^4.0.1" 1264 | } 1265 | }, 1266 | "wordwrap": { 1267 | "version": "1.0.0", 1268 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1269 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" 1270 | }, 1271 | "wrappy": { 1272 | "version": "1.0.2", 1273 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1274 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1275 | "dev": true 1276 | }, 1277 | "xml-name-validator": { 1278 | "version": "2.0.1", 1279 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", 1280 | "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=" 1281 | } 1282 | } 1283 | } 1284 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grabity", 3 | "version": "1.0.5", 4 | "description": "Get preview data from a link. Just grab it.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "test-server": "node ./test_setup/server" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/e-oj/grabity" 13 | }, 14 | "keywords": [ 15 | "open graph", 16 | "twitter cards", 17 | "link preview", 18 | "url preview", 19 | "link", 20 | "url" 21 | ], 22 | "author": "Emmanuel Olaojo", 23 | "license": "MIT", 24 | "dependencies": { 25 | "jsdom": "11.3.0" 26 | }, 27 | "devDependencies": { 28 | "chai": "4.1.2", 29 | "express": "4.16.2", 30 | "mocha": "4.0.1", 31 | "morgan": "1.9.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/18/17 4 | */ 5 | 6 | let chai = require("chai") 7 | , expect = chai.expect; 8 | 9 | let grabity = require("../lib/grabity"); 10 | let {PORT} = require("../test_setup/config"); 11 | let tags = require("../test_setup/tags"); 12 | 13 | let URL = `http://localhost:${PORT}/test`; 14 | 15 | describe("grabity", () => { 16 | describe("#grabIt", () => { 17 | it("should return title, description, image, and favicon", async () => { 18 | let it = await grabity.grabIt(URL); 19 | 20 | expect(it.title).to.equal(tags["og:title"]); 21 | expect(it.description).to.equal(tags["og:description"]); 22 | expect(it.image).to.equal(tags["twitter:image"]); 23 | expect(it.favicon).to.equal("https://assets-cdn.github.com/favicon.ico"); 24 | }); 25 | }); 26 | 27 | describe("#grab", () => { 28 | it("should return all 'twitter:' and 'og:' tags, favicon, default title tag and meta description", async () => { 29 | let it = await grabity.grab(URL); 30 | let keys = Object.keys(tags); 31 | 32 | for(key of keys){ 33 | expect(it[key]).to.equal(tags[key]); 34 | } 35 | 36 | expect(it.favicon).to.equal("https://assets-cdn.github.com/favicon.ico"); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test_setup/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/18/17 4 | */ 5 | 6 | exports.PORT = 9973; 7 | -------------------------------------------------------------------------------- /test_setup/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/18/17 4 | */ 5 | 6 | let path = require("path"); 7 | let express = require("express"); 8 | let logger = require("morgan"); 9 | 10 | let {PORT} = require("./config"); 11 | 12 | let app = express(); 13 | 14 | app.use(logger("dev")); 15 | 16 | app.route("/test").get( (req, res) => { 17 | res.sendFile(path.join(__dirname, "test.html"), err => { 18 | if(err) throw err; 19 | }); 20 | }); 21 | 22 | app.listen(PORT); 23 | console.log(`grabity's test files served on port ${PORT}`); -------------------------------------------------------------------------------- /test_setup/tags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/18/17 4 | */ 5 | 6 | exports["og:title"] = "The Rock"; 7 | exports["og:type"] = "video.movie"; 8 | exports["og:url"] = "http://www.imdb.com/title/tt0117500/"; 9 | exports["og:description"] = "Dwayne Johnson?"; 10 | 11 | exports["twitter:card"] = "summary"; 12 | exports["twitter:site"] = "@flickr"; 13 | exports["twitter:title"] = "Small Island Developing States Photo Submission"; 14 | exports["twitter:description"] = "View the album on Flickr."; 15 | exports["twitter:image"] = "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg"; 16 | 17 | exports["title"] = "YE OLDE TEST FILE"; 18 | exports["description"] = "This is a meta description"; -------------------------------------------------------------------------------- /test_setup/test.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | <head> 4 | <meta charset="UTF-8"> 5 | <title>YE OLDE TEST FILE 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Here be a test document. 25 | 26 | -------------------------------------------------------------------------------- /utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author EmmanuelOlaojo 3 | * @since 11/18/17 4 | */ 5 | 6 | let jsdom = require("jsdom"); 7 | const {JSDOM} = jsdom; 8 | const virtualConsole = new jsdom.VirtualConsole(); 9 | 10 | const OG_PROP = "property"; 11 | const TWITTER_PROP = "name"; 12 | const CONTENT = "content"; 13 | 14 | virtualConsole.sendTo(console, {omitJSDOMErrors: true}); 15 | 16 | /** 17 | * Gets og/twitter title, description, image 18 | * from a url 19 | * 20 | * @param url the url 21 | * 22 | * @returns {Promise.<{og: {}, twitter: {}}>} 23 | */ 24 | exports.grabInfo = async (url) => { 25 | let og = {}; 26 | let twitter = {}; 27 | let defaults = {}; 28 | 29 | try{ 30 | let dom = await JSDOM.fromURL(url, {virtualConsole}); 31 | let doc = dom.window.document; 32 | let metaEls = doc.getElementsByTagName("meta"); 33 | let linkEls = doc.getElementsByTagName("link"); 34 | let titleTag = doc.getElementsByTagName("title"); 35 | let metaDescTag = doc.querySelector("meta[name='description']"); 36 | 37 | if(titleTag.length){ 38 | defaults.title = titleTag[0].textContent; 39 | } 40 | 41 | if(metaDescTag){ 42 | defaults.description = metaDescTag.content; 43 | } 44 | 45 | for(let meta of metaEls){ 46 | filterInfo(meta, OG_PROP, og); 47 | filterInfo(meta, TWITTER_PROP, twitter); 48 | } 49 | 50 | let favicon = findFavicon(linkEls); 51 | 52 | return {og, twitter, favicon, defaults}; 53 | } 54 | catch(err){ 55 | throw err; 56 | } 57 | }; 58 | 59 | /** 60 | * Gets all Open Graph and Twitter Card 61 | * properties from a url 62 | * 63 | * @param url the url 64 | * 65 | * @returns {Promise.<*>} 66 | */ 67 | exports.grabAll = async (url) => { 68 | let res = {}; 69 | 70 | try { 71 | let dom = await JSDOM.fromURL(url, {virtualConsole}); 72 | let doc = dom.window.document; 73 | let metaEls = doc.getElementsByTagName("meta"); 74 | let linkEls = doc.getElementsByTagName("link"); 75 | let titleTag = doc.getElementsByTagName("title"); 76 | let metaDescTag = doc.querySelector("meta[name='description']"); 77 | 78 | if(titleTag.length){ 79 | res.title = titleTag[0].textContent; 80 | } 81 | 82 | if(metaDescTag){ 83 | res.description = metaDescTag.content; 84 | } 85 | 86 | for(let meta of metaEls){ 87 | filterAll(meta, "og:", res); 88 | filterAll(meta, "twitter:", res); 89 | } 90 | 91 | res.favicon = findFavicon(linkEls); 92 | 93 | return res; 94 | } 95 | catch(err){ 96 | throw err; 97 | } 98 | }; 99 | 100 | /** 101 | * Try to find a valid favicon from all of the dom's links 102 | * 103 | * @param links an array of dom 'link' elements 104 | * @returns the href of the favicon, if found 105 | */ 106 | function findFavicon(links){ 107 | let favicon = ''; 108 | 109 | // Prioritise links with rel of 'icon' 110 | for(let link of links){ 111 | if (link.rel === 'icon' && link.href){ 112 | favicon = link.href; 113 | } 114 | } 115 | 116 | // Check links with rel including 'icon' 117 | if (!favicon) { 118 | for(let link of links){ 119 | if (link.rel.includes('icon') && link.href){ 120 | favicon = link.href; 121 | } 122 | } 123 | } 124 | 125 | // Check links with href containing 'favicon' 126 | if (!favicon) { 127 | for(let link of links){ 128 | if (link.href.includes('favicon')){ 129 | favicon = link.href; 130 | } 131 | } 132 | } 133 | 134 | return favicon 135 | } 136 | 137 | /** 138 | * Filter for all og and twitter properties 139 | * in a meta tag 140 | * 141 | * @param meta the meta dom element 142 | * @param prefix "og:" or "twitter:" 143 | * @param resObj properties attached here 144 | */ 145 | function filterAll(meta, prefix, resObj){ 146 | let prop = prefix === "og:" ? OG_PROP : TWITTER_PROP; 147 | 148 | if(meta.hasAttribute(prop)){ 149 | let tag = meta.getAttribute(prop); 150 | 151 | if(tag.startsWith(prefix)){ 152 | resObj[tag] = meta.getAttribute(CONTENT); 153 | } 154 | } 155 | } 156 | 157 | /** 158 | * Filter for og/twitter title, image, and 159 | * description properties in a meta tag 160 | * 161 | * @param meta the meta dom element 162 | * @param _prop OG_PROP or TWITTER_PROP 163 | * @param resObj properties attached here 164 | */ 165 | function filterInfo(meta, _prop, resObj){ 166 | if(!meta.hasAttribute(_prop)) return; 167 | 168 | let prop = meta.getAttribute(_prop); 169 | let ogTags = ["og:title", "og:description", "og:image"]; 170 | let twitterTags = ["twitter:title", "twitter:description", "twitter:image"]; 171 | let tags = _prop === OG_PROP ? ogTags : twitterTags; 172 | 173 | 174 | for(let tag of tags){ 175 | if (prop !== tag) continue; 176 | 177 | resObj[prop.split(":")[1]] = meta.getAttribute(CONTENT); 178 | } 179 | } 180 | --------------------------------------------------------------------------------