├── .gitignore ├── Procfile ├── README.md ├── bin └── screenshotAll ├── cloudflare-config.js ├── cloudinary-config.js ├── main.js ├── modules ├── format-mdn-feature-title.js ├── get-feature-list.js ├── get-mdn-browser-compat-data.js ├── screenshot-all.js ├── take-screenshot.js ├── take-screenshots.js ├── trim-screenshot.js ├── update-cloudflare.js ├── upload-screenshot.js └── upload-screenshots.js ├── package-lock.json ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cloudinary-config.secrets.js 3 | cloudflare-config.secrets.js 4 | .idea/ 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node main.js 2 | worker: node modules/screenshot-all.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CanIUse Embed API 2 | 3 | An API for doing things with my [CanIUse Embed](https://caniuse.bitsofco.de/) 4 | -------------------------------------------------------------------------------- /bin/screenshotAll: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | const screenshotAll = require("../modules/screenshot-all"); 6 | 7 | screenshotAll(); 8 | -------------------------------------------------------------------------------- /cloudflare-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | email: process.env.CLOUDFLARE_EMAIL, 3 | api_key: process.env.CLOUDFLARE_API_KEY, 4 | zone_id: process.env.CLOUDFLARE_ZONE_ID 5 | }; 6 | -------------------------------------------------------------------------------- /cloudinary-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 3 | api_key: process.env.CLOUDINARY_API_KEY, 4 | api_secret: process.env.CLOUDINARY_API_SECRET 5 | }; 6 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* ************************************** 2 | 3 | CanIUse Embed Screenshot 4 | 5 | ************************************** */ 6 | 7 | const express = require("express"); 8 | const bodyParser = require("body-parser"); 9 | const app = express(); 10 | 11 | const takeScreenshot = require("./modules/take-screenshot"); 12 | const trimScreenshot = require("./modules/trim-screenshot"); 13 | const uploadScreenshot = require("./modules/upload-screenshot"); 14 | 15 | const getFeatureList = require("./modules/get-feature-list"); 16 | const getMDNBrowserCompatData = require("./modules/get-mdn-browser-compat-data"); 17 | 18 | const allowedOrigins = process.env.ALLOWED_ORIGINS; 19 | console.log("Allowed origins: ", process.env.ALLOWED_ORIGINS); 20 | 21 | app.set('port', (process.env.PORT || 3000)); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({extended: true})); 24 | 25 | app.use(function (req, res, next) { 26 | res.header("Access-Control-Allow-Origin", "*"); 27 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 28 | next(); 29 | }); 30 | 31 | app.get("/", (req, res) => { 32 | res.status(200).send("Welcome to the CanIUse Embed API!"); 33 | }); 34 | 35 | app.post("/capture", async (req, res) => { 36 | 37 | const requestOrigin = req.get('origin'); 38 | console.log("Request from:", requestOrigin); 39 | 40 | if (allowedOrigins && !allowedOrigins.includes(requestOrigin)) return res.status("400").json({body: "Unauthorized host"}); 41 | if (!req.body) return res.status("400").send("Need request params"); 42 | if (!req.body.feature) return res.status("400").send("Feature required"); 43 | 44 | const feature = req.body.feature; 45 | const periods = req.body.periods; 46 | const accessibleColours = req.body.accessibleColours === "true"; 47 | 48 | console.log(feature); 49 | console.log(periods); 50 | console.log(accessibleColours); 51 | 52 | try { 53 | let screenshot = await takeScreenshot(feature, periods, accessibleColours); 54 | screenshot = await trimScreenshot(screenshot); 55 | screenshot = await uploadScreenshot(feature, screenshot, { 56 | folder: 'caniuse-embed/static', 57 | public_id: `${feature}-${new Date().getTime()}` 58 | }); 59 | res.status(200).json(screenshot); 60 | } catch (err) { 61 | console.error(err); 62 | res.status("500").json(err); 63 | } 64 | }); 65 | 66 | app.get("/features", async (req, res) => { 67 | const features = await getFeatureList(); 68 | res.status(200).json(features); 69 | }); 70 | 71 | app.post("/mdn-browser-compat-data", async (req, res) => { 72 | try { 73 | const feature = req && req.body && req.body.feature; 74 | const data = await getMDNBrowserCompatData(feature); 75 | res.status(200).json(data); 76 | } catch (err) { 77 | console.error(err); 78 | res.status("500").json(err); 79 | } 80 | }); 81 | 82 | const server = app.listen(app.get('port'), function () { 83 | console.log("app running on port.", server.address().port); 84 | }); 85 | -------------------------------------------------------------------------------- /modules/format-mdn-feature-title.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = async (path) => { 3 | 4 | let title = null; 5 | 6 | switch (path[0]) { 7 | 8 | case 'api': 9 | if (path.length !== 2) break; 10 | 11 | title = `${path[1]} API`; 12 | 13 | break; 14 | 15 | case 'css': 16 | 17 | if (path.length === 3) { 18 | if (path[1] === 'at-rules') title = `CSS at-rule: ${path[2]}`; 19 | if (path[1] === 'properties') title = `CSS Property: ${path[2]}`; 20 | if (path[1] === 'selectors') title = `CSS Selector: ${path[2]}`; 21 | if (path[1] === 'types') title = `CSS Data Type: ${path[2]}`; 22 | } 23 | 24 | else if (path.length === 4 && path[1] === 'properties') { 25 | title = `CSS Property: ${path[2]}:${path[3]}`; 26 | } 27 | 28 | break; 29 | 30 | case 'html': 31 | if (path.length !== 3 && path.length !== 4) break; 32 | 33 | if (path[1] === 'manifest') title = `Web App Manifest Property: ${path[2]}`; 34 | if (path[1] === 'global_attributes') title = `Global HTML Attribute: ${path[2]}`; 35 | 36 | if (path[1] === 'elements' && path[2] !== 'input') title = `HTML Element: ${path[2]}`; 37 | if (path[1] === 'elements' && path[2] === 'input') title = `HTML Element: ${path[3]}`; 38 | 39 | break; 40 | 41 | case 'http': 42 | if (path.length !== 3) break; 43 | 44 | if (path[1] === 'methods') title = `HTTP Method: ${path[2]}`; 45 | if (path[1] === 'status') title = `HTTP Status: ${path[2]}`; 46 | if (path[1] === 'headers') title = `HTTP Header: ${path[2]}`; 47 | 48 | break; 49 | 50 | case 'javascript': 51 | 52 | title = `Javascript ${ path[ path.length - 1 ] }`; 53 | 54 | break; 55 | 56 | case 'mathml': 57 | if (path.length !== 3) break; 58 | 59 | if (path[1] === 'elements') title = `MathML Element: ${path[2]}`; 60 | 61 | break; 62 | 63 | case 'svg': 64 | if (path.length !== 3 && path.length !== 4) break; 65 | 66 | if (path[1] === 'elements') title = `SVG Element: ${path[2]}`; 67 | 68 | if (path[1] === 'attributes' && path.length === 2) title = `SVG Attribute: ${path[2]}`; 69 | if (path[1] === 'attributes' && path.length === 4) title = `SVG Attribute: ${path[3]}`; 70 | 71 | break; 72 | 73 | case 'webextensions': 74 | if (path.length !== 3 && path.length !== 4) break; 75 | 76 | if (path[1] === 'manifest' && path.length === 3) title = `WebExtension Manifest Property: ${path[2]}`; 77 | 78 | if (path[1] === 'api' && path.length === 4) title = `WebExtensions API: ${path[2]} ${path[3]}`; 79 | 80 | break; 81 | } 82 | 83 | if (!title) title = path.join(' '); 84 | return title; 85 | }; 86 | -------------------------------------------------------------------------------- /modules/get-feature-list.js: -------------------------------------------------------------------------------- 1 | const bcd = require('mdn-browser-compat-data'); 2 | const fetch = require('node-fetch'); 3 | 4 | const formatMDNTitle = require('./format-mdn-feature-title'); 5 | 6 | const sort_by = (field, primer) => { 7 | // http://stackoverflow.com/a/979325 8 | var key = primer ? 9 | function(x) {return primer(x[field])} : 10 | function(x) {return x[field]}; 11 | return function (a, b) { 12 | return a = key(a), b = key(b), 1 * ((a > b) - (b > a)); 13 | } 14 | }; 15 | 16 | const capitalizeFirstLetter = (string) => { 17 | return string.charAt(0).toUpperCase() + string.slice(1); 18 | }; 19 | 20 | const getMDNData = async () => { 21 | const finalPaths = []; 22 | 23 | const traverseObject = (obj, objPath) => { 24 | for (let key in obj) { 25 | if (obj.hasOwnProperty(key)) { 26 | 27 | const newPath = [...objPath, key]; 28 | 29 | if ( obj[key]['__compat'] ) { 30 | finalPaths.push(newPath); 31 | } else { 32 | traverseObject(obj[key], newPath); 33 | } 34 | 35 | } 36 | } 37 | return null; 38 | }; 39 | 40 | Object.keys(bcd).forEach((category) => { 41 | if (category === "browsers") return; 42 | traverseObject(bcd[category], [category]); 43 | }); 44 | 45 | const features = []; 46 | const excludedCategories = ['webdriver', 'xpath', 'xslt']; 47 | 48 | finalPaths.forEach(async (path) => { 49 | 50 | if (excludedCategories.includes(path[0])) return; 51 | 52 | const feature = { 53 | id: 'mdn-' + path.join('__'), // @separator 54 | title: await formatMDNTitle(path), 55 | dataSource: 'mdn' 56 | }; 57 | 58 | features.push(feature); 59 | }); 60 | 61 | return features; 62 | }; 63 | 64 | const getCanIUseData = async () => { 65 | 66 | const url = 'https://raw.githubusercontent.com/Fyrd/caniuse/master/fulldata-json/data-2.0.json'; 67 | const options = { 68 | headers: { 69 | 'Content-Type': 'application/json', 70 | } 71 | }; 72 | 73 | return fetch(url, options) 74 | .then((res) => res.json()) 75 | .then((res) => { 76 | 77 | const features = []; 78 | 79 | for (let key in res.data) { 80 | if (res.data.hasOwnProperty(key)) { 81 | 82 | const feature = { 83 | id: key, 84 | title: res.data[key].title, 85 | dataSource: 'caniuse' 86 | }; 87 | 88 | feature.title = capitalizeFirstLetter(feature.title); 89 | 90 | features.push(feature); 91 | } 92 | } 93 | 94 | return features; 95 | }); 96 | }; 97 | 98 | 99 | module.exports = async () => { 100 | 101 | const mdn = await getMDNData(); 102 | const ciu = await getCanIUseData(); 103 | const features = [...ciu, ...mdn]; 104 | 105 | features.sort(sort_by('title', function(a){return a})); 106 | 107 | return features; 108 | }; 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /modules/get-mdn-browser-compat-data.js: -------------------------------------------------------------------------------- 1 | const bcd = require('mdn-browser-compat-data'); 2 | const formatMDNTitle = require('./format-mdn-feature-title'); 3 | 4 | module.exports = async (feature) => { 5 | if (!feature) return bcd; 6 | 7 | const path = feature.split('mdn-')[1].split('__'); 8 | 9 | let obj = bcd; 10 | for (let i = 0; i < path.length; i++) { 11 | obj = obj[ path[i] ]; 12 | } 13 | 14 | const compat = obj['__compat']; 15 | compat.title = await formatMDNTitle(path); 16 | 17 | return compat || bcd; 18 | }; 19 | -------------------------------------------------------------------------------- /modules/screenshot-all.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const uploadScreenshots = require("./upload-screenshots"); 3 | const takeScreenshots = require("./take-screenshots"); 4 | const updateCloudflarePageRule = require('./update-cloudflare'); 5 | 6 | const url = 'https://raw.githubusercontent.com/Fyrd/caniuse/master/fulldata-json/data-2.0.json'; 7 | 8 | fetch(url) 9 | .then((res) => res.json()) 10 | .then(async (res) => { 11 | 12 | let features = Object.keys(res.data); 13 | //features = features.slice(0, 20); // @testing 14 | 15 | console.log('***************************'); 16 | console.log('* STARTING screenshot-all *'); 17 | console.log('* ' + features.length + ' features *'); 18 | console.log('***************************'); 19 | 20 | const screenshots = await takeScreenshots(features); 21 | const images = await uploadScreenshots(screenshots); 22 | const cloudflareUpdated = await updateCloudflarePageRule(); 23 | 24 | console.log('***************************'); 25 | console.log('* FINAL LOG *'); 26 | console.log('***************************'); 27 | console.log(`${features.length} features attempted to capture and upload`); 28 | console.log(`${screenshots.length} features successfully captured`); 29 | console.log(`${images.length} features successfully uploaded`); 30 | console.log(`${features.length - images.length} errors`); 31 | console.log(`${cloudflareUpdated ? 'Cloudflare page rule successfully updated' : 'Unable to update cloudflare page rule'}`); 32 | console.log(''); 33 | images.forEach((image) => { 34 | console.log(image.url); 35 | }); 36 | console.log('***************************'); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /modules/take-screenshot.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | 3 | module.exports = async (feature, periods, accessibleColours) => { 4 | const browser = await puppeteer.launch({ 5 | args: ['--no-sandbox', '--disable-setuid-sandbox'], 6 | defaultViewport: { 7 | width: 800, 8 | height: 600, 9 | isLandscape: true 10 | } 11 | }); 12 | 13 | const page = await browser.newPage(); 14 | 15 | await page.goto( 16 | `https://caniuse.bitsofco.de/embed/index.html?feat=${feature}&periods=${periods}&accessible-colours=${accessibleColours}&screenshot=true`, 17 | { waitUntil: 'networkidle2' } 18 | ); 19 | 20 | const screenshot = await page.screenshot({ 21 | omitBackground: true, 22 | encoding: 'binary' 23 | }); 24 | 25 | await browser.close(); 26 | 27 | return screenshot; 28 | } 29 | -------------------------------------------------------------------------------- /modules/take-screenshots.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | const trimScreenshot = require('./trim-screenshot'); 3 | 4 | module.exports = async (features) => { 5 | 6 | const browser = await puppeteer.launch({ 7 | args: ['--no-sandbox', '--disable-setuid-sandbox'], 8 | defaultViewport: { 9 | width: 800, 10 | height: 600, 11 | isLandscape: true 12 | } 13 | }); 14 | 15 | const page = await browser.newPage(); 16 | 17 | const screenshots = []; 18 | 19 | for (let i = 0; i < features.length; i++) { 20 | const feature = features[i]; 21 | 22 | try { 23 | await page.goto( 24 | `https://caniuse.bitsofco.de/embed/index.html?feat=${feature}&screenshot=true`, 25 | { 26 | waitUntil: 'networkidle2' 27 | } 28 | ); 29 | 30 | let screenshot = await page.screenshot({ 31 | omitBackground: true, 32 | encoding: 'binary' 33 | }); 34 | 35 | console.log(`Screenshot captured of ${feature}!`); 36 | 37 | screenshot = await trimScreenshot(screenshot); 38 | 39 | console.log(`Screenshot trimmed of ${feature}!`); 40 | 41 | screenshots.push({ 42 | feature: feature, 43 | screenshot: screenshot 44 | }); 45 | 46 | } catch (err) { 47 | console.error(`Unable to capture screenshot of ${feature}`); 48 | } 49 | 50 | } // end for loop 51 | 52 | await browser.close(); 53 | 54 | return screenshots; 55 | } 56 | -------------------------------------------------------------------------------- /modules/trim-screenshot.js: -------------------------------------------------------------------------------- 1 | const gm = require('gm').subClass({imageMagick: true}); 2 | 3 | module.exports = (image) => { 4 | return new Promise((resolve, reject) => { 5 | 6 | gm(image, 'image.png') 7 | .trim() 8 | .toBuffer('PNG',function (err, buffer) { 9 | if (err) reject(err); 10 | resolve(buffer); 11 | }); 12 | 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /modules/update-cloudflare.js: -------------------------------------------------------------------------------- 1 | let config; 2 | 3 | if (process.env.CLOUDFLARE_EMAIL) { 4 | config = require("../cloudflare-config"); 5 | } else { 6 | config = require("../cloudflare-config.secrets"); 7 | } 8 | 9 | const fetch = require('node-fetch'); 10 | const defaultHeaders = { 11 | 'Content-Type': 'application/json', 12 | 'X-Auth-Email': config.email, 13 | 'X-Auth-Key': config.api_key 14 | }; 15 | 16 | const cloudflareAPIBase = 'https://api.cloudflare.com/client/v4'; 17 | 18 | const fetchPageRule = () => { 19 | const pageRuleId = 'bfb990382de1cfadb25b0dec7c113b27'; 20 | 21 | const url = `${cloudflareAPIBase}/zones/${config.zone_id}/pagerules`; 22 | const options = { headers: defaultHeaders }; 23 | 24 | return fetch(url, options) 25 | .then((res) => res.json()) 26 | .then((res) => { 27 | const pageRule = res.result.find((rule) => rule.id == pageRuleId); 28 | return pageRule; 29 | }); 30 | } 31 | 32 | const updatePageRule = (pageRule) => { 33 | 34 | const url = `${cloudflareAPIBase}/zones/${config.zone_id}/pagerules/${pageRule.id}`; 35 | const options = { 36 | method: 'PUT', 37 | headers: defaultHeaders, 38 | body: JSON.stringify({ 39 | targets: pageRule.targets, 40 | actions: [{ 41 | id: pageRule.actions[0].id, 42 | value: { 43 | url: `https://res.cloudinary.com/ireaderinokun/image/upload/v${new Date().getTime()}/caniuse-embed/all/$1.$2`, 44 | status_code: 302 45 | } 46 | }] 47 | }) 48 | }; 49 | 50 | return fetch(url, options) 51 | .then((res) => res.json()) 52 | .then((res) => console.log(res)); 53 | }; 54 | 55 | module.exports = async () => { 56 | try { 57 | const pageRule = await fetchPageRule(); 58 | await updatePageRule(pageRule); 59 | return true; 60 | } catch(err) { 61 | console.log(err); 62 | return false; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /modules/upload-screenshot.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require('cloudinary').v2; 2 | 3 | if (process.env.CLOUDINARY_CLOUD_NAME) { 4 | cloudinary.config(require("../cloudinary-config")); 5 | } else { 6 | cloudinary.config(require("../cloudinary-config.secrets")); 7 | } 8 | 9 | module.exports = (feature, screenshot, options) => { 10 | return new Promise((resolve, reject) => { 11 | 12 | options = options || { 13 | folder: 'caniuse-embed', 14 | public_id: `${feature}-${new Date().getTime()}` 15 | }; 16 | 17 | cloudinary.uploader.upload_stream(options, (error, result) => { 18 | if (error) reject(error) 19 | else resolve(result); 20 | }).end(screenshot); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /modules/upload-screenshots.js: -------------------------------------------------------------------------------- 1 | const uploadScreenshot = require("./upload-screenshot"); 2 | 3 | module.exports = async (screenshots) => { 4 | 5 | const images = []; 6 | 7 | for (let i = 0; i < screenshots.length; i++) { 8 | 9 | const screenshot = screenshots[i]; 10 | 11 | try { 12 | 13 | const options = { 14 | folder: 'caniuse-embed/all', 15 | public_id: screenshot.feature 16 | }; 17 | const image = await uploadScreenshot(screenshot.feature, screenshot.screenshot, options); 18 | 19 | console.log(`Screenshot uploaded of ${screenshot.feature}!`); 20 | 21 | images.push({ 22 | feature: screenshot.feature, 23 | url: image.secure_url 24 | }); 25 | 26 | } catch (err) { 27 | console.error(`Unable to upload screenshot of ${screenshot.feature}`); 28 | } 29 | 30 | } // end for loop 31 | 32 | return images; 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "caniuse-embed-screenshot-api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "array-parallel": { 22 | "version": "0.1.3", 23 | "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", 24 | "integrity": "sha1-j3hTCJJu1apHjEfmTRszS2wMlH0=" 25 | }, 26 | "array-series": { 27 | "version": "0.1.5", 28 | "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", 29 | "integrity": "sha1-3103v8XC7wdV4qpPkv6ufUtaly8=" 30 | }, 31 | "async-limiter": { 32 | "version": "1.0.0", 33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", 34 | "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" 35 | }, 36 | "balanced-match": { 37 | "version": "1.0.0", 38 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 39 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 40 | }, 41 | "body-parser": { 42 | "version": "1.18.3", 43 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 44 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 45 | "requires": { 46 | "bytes": "3.0.0", 47 | "content-type": "~1.0.4", 48 | "debug": "2.6.9", 49 | "depd": "~1.1.2", 50 | "http-errors": "~1.6.3", 51 | "iconv-lite": "0.4.23", 52 | "on-finished": "~2.3.0", 53 | "qs": "6.5.2", 54 | "raw-body": "2.3.3", 55 | "type-is": "~1.6.16" 56 | }, 57 | "dependencies": { 58 | "debug": { 59 | "version": "2.6.9", 60 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 61 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 62 | "requires": { 63 | "ms": "2.0.0" 64 | } 65 | }, 66 | "ms": { 67 | "version": "2.0.0", 68 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 69 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 70 | } 71 | } 72 | }, 73 | "brace-expansion": { 74 | "version": "1.1.11", 75 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 76 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 77 | "requires": { 78 | "balanced-match": "^1.0.0", 79 | "concat-map": "0.0.1" 80 | } 81 | }, 82 | "buffer-from": { 83 | "version": "1.1.1", 84 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 85 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 86 | }, 87 | "bytes": { 88 | "version": "3.0.0", 89 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 90 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 91 | }, 92 | "cloudinary": { 93 | "version": "1.13.2", 94 | "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-1.13.2.tgz", 95 | "integrity": "sha512-mZWxWxgln6Bkk5QA72BGb1mwRBFs+r94ZzdN37gROPL7FSOslhvFfEvF/Rz/ruPqR7mblMdzVz3wuLQi1oCalw==", 96 | "requires": { 97 | "lodash": "^4.17.11", 98 | "q": "^1.5.1" 99 | } 100 | }, 101 | "concat-map": { 102 | "version": "0.0.1", 103 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 104 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 105 | }, 106 | "concat-stream": { 107 | "version": "1.6.2", 108 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 109 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 110 | "requires": { 111 | "buffer-from": "^1.0.0", 112 | "inherits": "^2.0.3", 113 | "readable-stream": "^2.2.2", 114 | "typedarray": "^0.0.6" 115 | } 116 | }, 117 | "content-disposition": { 118 | "version": "0.5.2", 119 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 120 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 121 | }, 122 | "content-type": { 123 | "version": "1.0.4", 124 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 125 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 126 | }, 127 | "cookie": { 128 | "version": "0.3.1", 129 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 130 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 131 | }, 132 | "cookie-signature": { 133 | "version": "1.0.6", 134 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 135 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 136 | }, 137 | "core-util-is": { 138 | "version": "1.0.2", 139 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 140 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 141 | }, 142 | "cross-spawn": { 143 | "version": "4.0.2", 144 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", 145 | "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", 146 | "requires": { 147 | "lru-cache": "^4.0.1", 148 | "which": "^1.2.9" 149 | } 150 | }, 151 | "debug": { 152 | "version": "3.2.6", 153 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 154 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 155 | "requires": { 156 | "ms": "^2.1.1" 157 | } 158 | }, 159 | "depd": { 160 | "version": "1.1.2", 161 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 162 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 163 | }, 164 | "destroy": { 165 | "version": "1.0.4", 166 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 167 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 168 | }, 169 | "ee-first": { 170 | "version": "1.1.1", 171 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 172 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 173 | }, 174 | "encodeurl": { 175 | "version": "1.0.2", 176 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 177 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 178 | }, 179 | "es6-promise": { 180 | "version": "4.2.5", 181 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", 182 | "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==" 183 | }, 184 | "es6-promisify": { 185 | "version": "5.0.0", 186 | "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 187 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 188 | "requires": { 189 | "es6-promise": "^4.0.3" 190 | } 191 | }, 192 | "escape-html": { 193 | "version": "1.0.3", 194 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 195 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 196 | }, 197 | "etag": { 198 | "version": "1.8.1", 199 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 200 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 201 | }, 202 | "express": { 203 | "version": "4.16.4", 204 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 205 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 206 | "requires": { 207 | "accepts": "~1.3.5", 208 | "array-flatten": "1.1.1", 209 | "body-parser": "1.18.3", 210 | "content-disposition": "0.5.2", 211 | "content-type": "~1.0.4", 212 | "cookie": "0.3.1", 213 | "cookie-signature": "1.0.6", 214 | "debug": "2.6.9", 215 | "depd": "~1.1.2", 216 | "encodeurl": "~1.0.2", 217 | "escape-html": "~1.0.3", 218 | "etag": "~1.8.1", 219 | "finalhandler": "1.1.1", 220 | "fresh": "0.5.2", 221 | "merge-descriptors": "1.0.1", 222 | "methods": "~1.1.2", 223 | "on-finished": "~2.3.0", 224 | "parseurl": "~1.3.2", 225 | "path-to-regexp": "0.1.7", 226 | "proxy-addr": "~2.0.4", 227 | "qs": "6.5.2", 228 | "range-parser": "~1.2.0", 229 | "safe-buffer": "5.1.2", 230 | "send": "0.16.2", 231 | "serve-static": "1.13.2", 232 | "setprototypeof": "1.1.0", 233 | "statuses": "~1.4.0", 234 | "type-is": "~1.6.16", 235 | "utils-merge": "1.0.1", 236 | "vary": "~1.1.2" 237 | }, 238 | "dependencies": { 239 | "debug": { 240 | "version": "2.6.9", 241 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 242 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 243 | "requires": { 244 | "ms": "2.0.0" 245 | } 246 | }, 247 | "ms": { 248 | "version": "2.0.0", 249 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 250 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 251 | }, 252 | "statuses": { 253 | "version": "1.4.0", 254 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 255 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 256 | } 257 | } 258 | }, 259 | "extend": { 260 | "version": "3.0.2", 261 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 262 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 263 | }, 264 | "extract-zip": { 265 | "version": "1.6.7", 266 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", 267 | "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", 268 | "requires": { 269 | "concat-stream": "1.6.2", 270 | "debug": "2.6.9", 271 | "mkdirp": "0.5.1", 272 | "yauzl": "2.4.1" 273 | }, 274 | "dependencies": { 275 | "debug": { 276 | "version": "2.6.9", 277 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 278 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 279 | "requires": { 280 | "ms": "2.0.0" 281 | } 282 | }, 283 | "ms": { 284 | "version": "2.0.0", 285 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 286 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 287 | } 288 | } 289 | }, 290 | "fd-slicer": { 291 | "version": "1.0.1", 292 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", 293 | "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", 294 | "requires": { 295 | "pend": "~1.2.0" 296 | } 297 | }, 298 | "finalhandler": { 299 | "version": "1.1.1", 300 | "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 301 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 302 | "requires": { 303 | "debug": "2.6.9", 304 | "encodeurl": "~1.0.2", 305 | "escape-html": "~1.0.3", 306 | "on-finished": "~2.3.0", 307 | "parseurl": "~1.3.2", 308 | "statuses": "~1.4.0", 309 | "unpipe": "~1.0.0" 310 | }, 311 | "dependencies": { 312 | "debug": { 313 | "version": "2.6.9", 314 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 315 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 316 | "requires": { 317 | "ms": "2.0.0" 318 | } 319 | }, 320 | "ms": { 321 | "version": "2.0.0", 322 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 323 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 324 | }, 325 | "statuses": { 326 | "version": "1.4.0", 327 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 328 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 329 | } 330 | } 331 | }, 332 | "forwarded": { 333 | "version": "0.1.2", 334 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 335 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 336 | }, 337 | "fresh": { 338 | "version": "0.5.2", 339 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 340 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 341 | }, 342 | "fs.realpath": { 343 | "version": "1.0.0", 344 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 345 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 346 | }, 347 | "glob": { 348 | "version": "7.1.3", 349 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 350 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 351 | "requires": { 352 | "fs.realpath": "^1.0.0", 353 | "inflight": "^1.0.4", 354 | "inherits": "2", 355 | "minimatch": "^3.0.4", 356 | "once": "^1.3.0", 357 | "path-is-absolute": "^1.0.0" 358 | } 359 | }, 360 | "gm": { 361 | "version": "1.23.1", 362 | "resolved": "https://registry.npmjs.org/gm/-/gm-1.23.1.tgz", 363 | "integrity": "sha1-Lt7rlYCE0PjqeYjl2ZWxx9/BR3c=", 364 | "requires": { 365 | "array-parallel": "~0.1.3", 366 | "array-series": "~0.1.5", 367 | "cross-spawn": "^4.0.0", 368 | "debug": "^3.1.0" 369 | } 370 | }, 371 | "http-errors": { 372 | "version": "1.6.3", 373 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 374 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 375 | "requires": { 376 | "depd": "~1.1.2", 377 | "inherits": "2.0.3", 378 | "setprototypeof": "1.1.0", 379 | "statuses": ">= 1.4.0 < 2" 380 | } 381 | }, 382 | "https-proxy-agent": { 383 | "version": "2.2.4", 384 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", 385 | "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", 386 | "requires": { 387 | "agent-base": "^4.3.0", 388 | "debug": "^3.1.0" 389 | }, 390 | "dependencies": { 391 | "agent-base": { 392 | "version": "4.3.0", 393 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 394 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 395 | "requires": { 396 | "es6-promisify": "^5.0.0" 397 | } 398 | } 399 | } 400 | }, 401 | "iconv-lite": { 402 | "version": "0.4.23", 403 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 404 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 405 | "requires": { 406 | "safer-buffer": ">= 2.1.2 < 3" 407 | } 408 | }, 409 | "inflight": { 410 | "version": "1.0.6", 411 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 412 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 413 | "requires": { 414 | "once": "^1.3.0", 415 | "wrappy": "1" 416 | } 417 | }, 418 | "inherits": { 419 | "version": "2.0.3", 420 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 421 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 422 | }, 423 | "ipaddr.js": { 424 | "version": "1.8.0", 425 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 426 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 427 | }, 428 | "isarray": { 429 | "version": "1.0.0", 430 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 431 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 432 | }, 433 | "isexe": { 434 | "version": "2.0.0", 435 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 436 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 437 | }, 438 | "lodash": { 439 | "version": "4.17.15", 440 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 441 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 442 | }, 443 | "lru-cache": { 444 | "version": "4.1.5", 445 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 446 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 447 | "requires": { 448 | "pseudomap": "^1.0.2", 449 | "yallist": "^2.1.2" 450 | } 451 | }, 452 | "mdn-browser-compat-data": { 453 | "version": "1.0.20", 454 | "resolved": "https://registry.npmjs.org/mdn-browser-compat-data/-/mdn-browser-compat-data-1.0.20.tgz", 455 | "integrity": "sha512-6sS4ydJ6+/ZnSwKUN8Jtu7NzD2pobXRolYRjHupKtRBDmx+8WZG1yUdOfF9QfCNPkWKrf3GcSFEurIrnJC56Qw==", 456 | "requires": { 457 | "extend": "3.0.2" 458 | } 459 | }, 460 | "media-typer": { 461 | "version": "0.3.0", 462 | "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 463 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 464 | }, 465 | "merge-descriptors": { 466 | "version": "1.0.1", 467 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 468 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 469 | }, 470 | "methods": { 471 | "version": "1.1.2", 472 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 473 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 474 | }, 475 | "mime": { 476 | "version": "2.3.1", 477 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", 478 | "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==" 479 | }, 480 | "mime-db": { 481 | "version": "1.37.0", 482 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 483 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 484 | }, 485 | "mime-types": { 486 | "version": "2.1.21", 487 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 488 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 489 | "requires": { 490 | "mime-db": "~1.37.0" 491 | } 492 | }, 493 | "minimatch": { 494 | "version": "3.0.4", 495 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 496 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 497 | "requires": { 498 | "brace-expansion": "^1.1.7" 499 | } 500 | }, 501 | "mkdirp": { 502 | "version": "0.5.1", 503 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 504 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 505 | "requires": { 506 | "minimist": "0.0.8" 507 | }, 508 | "dependencies": { 509 | "minimist": { 510 | "version": "0.0.8", 511 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 512 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 513 | } 514 | } 515 | }, 516 | "ms": { 517 | "version": "2.1.1", 518 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 519 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 520 | }, 521 | "negotiator": { 522 | "version": "0.6.1", 523 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 524 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 525 | }, 526 | "node-fetch": { 527 | "version": "2.6.0", 528 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", 529 | "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" 530 | }, 531 | "on-finished": { 532 | "version": "2.3.0", 533 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 534 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 535 | "requires": { 536 | "ee-first": "1.1.1" 537 | } 538 | }, 539 | "once": { 540 | "version": "1.4.0", 541 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 542 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 543 | "requires": { 544 | "wrappy": "1" 545 | } 546 | }, 547 | "parseurl": { 548 | "version": "1.3.2", 549 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 550 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 551 | }, 552 | "path-is-absolute": { 553 | "version": "1.0.1", 554 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 555 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 556 | }, 557 | "path-to-regexp": { 558 | "version": "0.1.7", 559 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 560 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 561 | }, 562 | "pend": { 563 | "version": "1.2.0", 564 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 565 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" 566 | }, 567 | "process-nextick-args": { 568 | "version": "2.0.0", 569 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 570 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 571 | }, 572 | "progress": { 573 | "version": "2.0.1", 574 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", 575 | "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==" 576 | }, 577 | "proxy-addr": { 578 | "version": "2.0.4", 579 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 580 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 581 | "requires": { 582 | "forwarded": "~0.1.2", 583 | "ipaddr.js": "1.8.0" 584 | } 585 | }, 586 | "proxy-from-env": { 587 | "version": "1.0.0", 588 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", 589 | "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" 590 | }, 591 | "pseudomap": { 592 | "version": "1.0.2", 593 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 594 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 595 | }, 596 | "puppeteer": { 597 | "version": "1.10.0", 598 | "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.10.0.tgz", 599 | "integrity": "sha512-3i28X/ucX8t3eL4TZA60FLMOQNKqudFSOGDHr0cT7T4dE027CrcS885aAqjdxNybhMPliM5yImNsKJ6SQrPzhw==", 600 | "requires": { 601 | "debug": "^3.1.0", 602 | "extract-zip": "^1.6.6", 603 | "https-proxy-agent": "^2.2.1", 604 | "mime": "^2.0.3", 605 | "progress": "^2.0.0", 606 | "proxy-from-env": "^1.0.0", 607 | "rimraf": "^2.6.1", 608 | "ws": "^5.1.1" 609 | }, 610 | "dependencies": { 611 | "ws": { 612 | "version": "5.2.2", 613 | "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", 614 | "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", 615 | "requires": { 616 | "async-limiter": "~1.0.0" 617 | } 618 | } 619 | } 620 | }, 621 | "q": { 622 | "version": "1.5.1", 623 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 624 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 625 | }, 626 | "qs": { 627 | "version": "6.5.2", 628 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 629 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 630 | }, 631 | "range-parser": { 632 | "version": "1.2.0", 633 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 634 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 635 | }, 636 | "raw-body": { 637 | "version": "2.3.3", 638 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 639 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 640 | "requires": { 641 | "bytes": "3.0.0", 642 | "http-errors": "1.6.3", 643 | "iconv-lite": "0.4.23", 644 | "unpipe": "1.0.0" 645 | } 646 | }, 647 | "readable-stream": { 648 | "version": "2.3.6", 649 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 650 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 651 | "requires": { 652 | "core-util-is": "~1.0.0", 653 | "inherits": "~2.0.3", 654 | "isarray": "~1.0.0", 655 | "process-nextick-args": "~2.0.0", 656 | "safe-buffer": "~5.1.1", 657 | "string_decoder": "~1.1.1", 658 | "util-deprecate": "~1.0.1" 659 | } 660 | }, 661 | "rimraf": { 662 | "version": "2.6.2", 663 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 664 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 665 | "requires": { 666 | "glob": "^7.0.5" 667 | } 668 | }, 669 | "safe-buffer": { 670 | "version": "5.1.2", 671 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 672 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 673 | }, 674 | "safer-buffer": { 675 | "version": "2.1.2", 676 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 677 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 678 | }, 679 | "send": { 680 | "version": "0.16.2", 681 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 682 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 683 | "requires": { 684 | "debug": "2.6.9", 685 | "depd": "~1.1.2", 686 | "destroy": "~1.0.4", 687 | "encodeurl": "~1.0.2", 688 | "escape-html": "~1.0.3", 689 | "etag": "~1.8.1", 690 | "fresh": "0.5.2", 691 | "http-errors": "~1.6.2", 692 | "mime": "1.4.1", 693 | "ms": "2.0.0", 694 | "on-finished": "~2.3.0", 695 | "range-parser": "~1.2.0", 696 | "statuses": "~1.4.0" 697 | }, 698 | "dependencies": { 699 | "debug": { 700 | "version": "2.6.9", 701 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 702 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 703 | "requires": { 704 | "ms": "2.0.0" 705 | } 706 | }, 707 | "mime": { 708 | "version": "1.4.1", 709 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 710 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 711 | }, 712 | "ms": { 713 | "version": "2.0.0", 714 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 715 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 716 | }, 717 | "statuses": { 718 | "version": "1.4.0", 719 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 720 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 721 | } 722 | } 723 | }, 724 | "serve-static": { 725 | "version": "1.13.2", 726 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 727 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 728 | "requires": { 729 | "encodeurl": "~1.0.2", 730 | "escape-html": "~1.0.3", 731 | "parseurl": "~1.3.2", 732 | "send": "0.16.2" 733 | } 734 | }, 735 | "setprototypeof": { 736 | "version": "1.1.0", 737 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 738 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 739 | }, 740 | "statuses": { 741 | "version": "1.5.0", 742 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 743 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 744 | }, 745 | "string_decoder": { 746 | "version": "1.1.1", 747 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 748 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 749 | "requires": { 750 | "safe-buffer": "~5.1.0" 751 | } 752 | }, 753 | "type-is": { 754 | "version": "1.6.16", 755 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 756 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 757 | "requires": { 758 | "media-typer": "0.3.0", 759 | "mime-types": "~2.1.18" 760 | } 761 | }, 762 | "typedarray": { 763 | "version": "0.0.6", 764 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 765 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 766 | }, 767 | "unpipe": { 768 | "version": "1.0.0", 769 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 770 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 771 | }, 772 | "util-deprecate": { 773 | "version": "1.0.2", 774 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 775 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 776 | }, 777 | "utils-merge": { 778 | "version": "1.0.1", 779 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 780 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 781 | }, 782 | "vary": { 783 | "version": "1.1.2", 784 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 785 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 786 | }, 787 | "which": { 788 | "version": "1.3.1", 789 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 790 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 791 | "requires": { 792 | "isexe": "^2.0.0" 793 | } 794 | }, 795 | "wrappy": { 796 | "version": "1.0.2", 797 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 798 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 799 | }, 800 | "yallist": { 801 | "version": "2.1.2", 802 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 803 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 804 | }, 805 | "yauzl": { 806 | "version": "2.4.1", 807 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", 808 | "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", 809 | "requires": { 810 | "fd-slicer": "~1.0.1" 811 | } 812 | } 813 | } 814 | } 815 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "caniuse-embed-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "engines": { 7 | "node": "8.11.3" 8 | }, 9 | "scripts": { 10 | "test": "node test.js", 11 | "start": "node main.js", 12 | "screenshot-all": "node modules/screenshot-all.js" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "body-parser": "^1.18.3", 18 | "cloudinary": "^1.13.2", 19 | "express": "^4.16.4", 20 | "gm": "^1.23.1", 21 | "mdn-browser-compat-data": "^1.0.20", 22 | "node-fetch": "^2.6.0", 23 | "puppeteer": "^1.10.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const takeScreenshot = require("./modules/take-screenshot"); 2 | const uploadScreenshot = require("./modules/upload-screenshot"); 3 | 4 | const feature = 'once-event-listener'; 5 | const periods = 'future_1,current,past_1,past_2'; 6 | const accessibleColours = 'false'; 7 | 8 | takeScreenshot(feature, periods, accessibleColours) 9 | .then((screenshot) => uploadScreenshot(feature, screenshot)) 10 | .then((result) => console.log(result)) 11 | .catch((err) => console.log(err)); 12 | --------------------------------------------------------------------------------