├── .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 | ## [](https://travis-ci.org/e-oj/grabity) [](https://www.npmjs.com/package/grabity) [](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 |
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 |
2 |
3 |
4 |
5 | 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 |
--------------------------------------------------------------------------------