diff --git a/.dockerignore b/.dockerignore index 7522f1ba..ac82f884 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,3 +5,4 @@ static test scripts docs +firefox diff --git a/.eslintignore b/.eslintignore index a5892099..a435bfcc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ -public/bundle.js -public/webcrypto-shim.js +public test/frontend/bundle.js -firefox \ No newline at end of file +firefox diff --git a/.gitignore b/.gitignore index e1f30354..dc0910df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ .DS_Store node_modules -public/bundle.js +public/upload.js +public/download.js public/version.json +public/l20n.min.js static/* !static/info.txt test/frontend/bundle.js diff --git a/.stylelintrc b/.stylelintrc index 3c593e83..c0c673c7 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,6 +1,6 @@ extends: stylelint-config-standard rules: - color-hex-case: upper + color-hex-case: lower declaration-colon-newline-after: null selector-list-comma-newline-after: null diff --git a/circle.yml b/circle.yml index ed714bbd..e25fa992 100644 --- a/circle.yml +++ b/circle.yml @@ -16,7 +16,7 @@ deployment: latest: branch: master commands: - - npm run predocker + - npm run build - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS - docker build -t mozilla/send:latest . - docker push mozilla/send:latest @@ -24,7 +24,7 @@ deployment: tag: /.*/ owner: mozilla commands: - - npm run predocker + - npm run build - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS - docker build -t mozilla/send:$CIRCLE_TAG . - docker push mozilla/send:$CIRCLE_TAG diff --git a/docker-compose.yml b/docker-compose.yml index 8274bde1..f72bf161 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,6 @@ services: ports: - "1443:1443" environment: - - P2P_REDIS_HOST=redis + - REDIS_HOST=redis redis: image: redis:alpine diff --git a/docs/docker.md b/docs/docker.md index f45b770f..f94b23b3 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -3,12 +3,22 @@ | Name | Description |------------------|-------------| | `PORT` | Port the server will listen on (defaults to 1443). -| `P2P_S3_BUCKET` | The S3 bucket name. -| `P2P_REDIS_HOST` | Host name of the Redis server. +| `S3_BUCKET` | The S3 bucket name. +| `REDIS_HOST` | Host name of the Redis server. +| `GOOGLE_ANALYTICS_ID` | Google Analytics ID +| `SENTRY_CLIENT` | Sentry Client ID +| `SENTRY_DSN` | Sentry DSN +| `MAX_FILE_SIZE` | in bytes (defaults to 2147483648) | `NODE_ENV` | "production" ## Example: ```sh -$ docker run --net=host -e 'NODE_ENV=production' -e 'P2P_S3_BUCKET=testpilot-p2p-dev' -e 'P2P_REDIS_HOST=dyf9s2r4vo3.bolxr4.0001.usw2.cache.amazonaws.com' mozilla/send:latest +$ docker run --net=host -e 'NODE_ENV=production' \ + -e 'S3_BUCKET=testpilot-p2p-dev' \ + -e 'REDIS_HOST=dyf9s2r4vo3.bolxr4.0001.usw2.cache.amazonaws.com' \ + -e 'GOOGLE_ANALYTICS_ID=UA-35433268-78' \ + -e 'SENTRY_CLIENT=https://51e23d7263e348a7a3b90a5357c61cb2@sentry.prod.mozaws.net/168' \ + -e 'SENTRY_DSN=https://51e23d7263e348a7a3b90a5357c61cb2:65e23d7263e348a7a3b90a5357c61c44@sentry.prod.mozaws.net/168' \ + mozilla/send:latest ``` diff --git a/docs/metrics.md b/docs/metrics.md index 035a57fa..54d27deb 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -112,6 +112,7 @@ Fired whenever a user deletes a file they’ve uploaded. - `cm6` - `cm7` - `cd1` +- `cd4` #### `copied` Fired whenever a user copies the URL of an upload file. diff --git a/frontend/src/common.js b/frontend/src/common.js new file mode 100644 index 00000000..5fa712a3 --- /dev/null +++ b/frontend/src/common.js @@ -0,0 +1,10 @@ +window.Raven = require('raven-js'); +window.Raven.config(window.dsn).install(); +window.dsn = undefined; + +const testPilotGA = require('testpilot-ga'); +window.analytics = new testPilotGA({ + an: 'Firefox Send', + ds: 'web', + tid: window.trackerId +}); diff --git a/frontend/src/download.js b/frontend/src/download.js index a6d96449..b0e05fe9 100644 --- a/frontend/src/download.js +++ b/frontend/src/download.js @@ -1,90 +1,175 @@ +require('./common'); const FileReceiver = require('./fileReceiver'); -const { notify } = require('./utils'); +const { notify, findMetric, gcmCompliant, sendEvent } = require('./utils'); +const bytes = require('bytes'); +const Storage = require('./storage'); +const storage = new Storage(localStorage); + const $ = require('jquery'); require('jquery-circle-progress'); const Raven = window.Raven; + $(document).ready(function() { + gcmCompliant().catch(err => { + $('#download').attr('hidden', true); + sendEvent('recipient', 'unsupported', { + cd6: err + }).then(() => { + location.replace('/unsupported'); + }); + }); //link back to homepage $('.send-new').attr('href', window.location.origin); - const filename = $('#dl-filename').html(); + $('.send-new').click(function(target) { + target.preventDefault(); + sendEvent('recipient', 'restarted', { + cd2: 'completed' + }).then(() => { + location.href = target.currentTarget.href; + }); + }); + + $('.legal-links a, .social-links a, #dl-firefox').click(function(target) { + target.preventDefault(); + const metric = findMetric(target.currentTarget.href); + // record exited event by recipient + sendEvent('recipient', 'exited', { + cd3: metric + }).then(() => { + location.href = target.currentTarget.href; + }); + }); + + const filename = $('#dl-filename').text(); + const bytelength = Number($('#dl-bytelength').text()); + const timeToExpiry = Number($('#dl-ttl').text()); //initiate progress bar $('#dl-progress').circleProgress({ value: 0.0, startAngle: -Math.PI / 2, - fill: '#00C8D7', + fill: '#3B9DFF', size: 158, animation: { duration: 300 } }); $('#download-btn').click(download); function download() { + storage.totalDownloads += 1; + const fileReceiver = new FileReceiver(); + const unexpiredFiles = storage.numFiles; fileReceiver.on('progress', progress => { + window.onunload = function() { + storage.referrer = 'cancelled-download'; + // record download-stopped (cancelled by tab close or reload) + sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd2: 'cancelled' + }); + }; + $('#download-page-one').attr('hidden', true); $('#download-progress').removeAttr('hidden'); const percent = progress[0] / progress[1]; // update progress bar $('#dl-progress').circleProgress('value', percent); - $('.percent-number').html(`${Math.floor(percent * 100)}`); - if (progress[1] < 1000000) { - $('.progress-text').html( - `${filename} (${(progress[0] / 1000).toFixed(1)}KB of - ${(progress[1] / 1000).toFixed(1)}KB)` - ); - } else if (progress[1] < 1000000000) { - $('.progress-text').html( - `${filename} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)` - ); - } else { - $('.progress-text').html( - `${filename} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)` - ); - } - //on complete - if (percent === 1) { - fileReceiver.removeAllListeners('progress'); - document.l10n.formatValues('downloadNotification', 'downloadFinish') - .then(translated => { - notify(translated[0]); - $('.title').html(translated[1]); - }); - } + $('.percent-number').text(`${Math.floor(percent * 100)}`); + $('.progress-text').text( + `${filename} (${bytes(progress[0], { + decimalPlaces: 1, + fixedDecimals: true + })} of ${bytes(progress[1], { decimalPlaces: 1 })})` + ); }); + let downloadEnd; fileReceiver.on('decrypting', isStillDecrypting => { // The file is being decrypted if (isStillDecrypting) { - console.log('Decrypting'); + fileReceiver.removeAllListeners('progress'); + window.onunload = null; + document.l10n.formatValue('decryptingFile').then(decryptingFile => { + $('.progress-text').text(decryptingFile); + }); } else { console.log('Done decrypting'); + downloadEnd = Date.now(); } }); fileReceiver.on('hashing', isStillHashing => { // The file is being hashed to make sure a malicious user hasn't tampered with it if (isStillHashing) { - console.log('Checking file integrity'); + document.l10n.formatValue('verifyingFile').then(verifyingFile => { + $('.progress-text').text(verifyingFile); + }); } else { - console.log('Integrity check done'); + $('.progress-text').text(' '); + document.l10n + .formatValues('downloadNotification', 'downloadFinish') + .then(translated => { + notify(translated[0]); + $('.title').text(translated[1]); + }); } }); + const startTime = Date.now(); + + // record download-started by recipient + sendEvent('recipient', 'download-started', { + cm1: bytelength, + cm4: timeToExpiry, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads + }); + fileReceiver .download() - .catch(() => { - document.l10n.formatValue('expiredPageHeader') - .then(translated => { - $('.title').text(translated); - }); + .catch(err => { + // record download-stopped (errored) by recipient + sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd2: 'errored', + cd6: err + }); + + document.l10n.formatValue('expiredPageHeader').then(translated => { + $('.title').text(translated); + }); $('#download-btn').attr('hidden', true); $('#expired-img').removeAttr('hidden'); console.log('The file has expired, or has already been deleted.'); return; }) .then(([decrypted, fname]) => { + const endTime = Date.now(); + const totalTime = endTime - startTime; + const downloadTime = endTime - downloadEnd; + const downloadSpeed = bytelength / (downloadTime / 1000); + + storage.referrer = 'completed-download'; + // record download-stopped (completed) by recipient + sendEvent('recipient', 'download-stopped', { + cm1: bytelength, + cm2: totalTime, + cm3: downloadSpeed, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd2: 'completed' + }); + const dataView = new DataView(decrypted); const blob = new Blob([dataView]); const downloadUrl = URL.createObjectURL(blob); diff --git a/frontend/src/fileReceiver.js b/frontend/src/fileReceiver.js index 155a8dd9..882dcb2b 100644 --- a/frontend/src/fileReceiver.js +++ b/frontend/src/fileReceiver.js @@ -58,41 +58,47 @@ class FileReceiver extends EventEmitter { true, ['encrypt', 'decrypt'] ) - ]).then(([fdata, key]) => { - this.emit('decrypting', true); - return Promise.all([ - window.crypto.subtle.decrypt( - { - name: 'AES-GCM', - iv: hexToArray(fdata.iv), - additionalData: hexToArray(fdata.aad) - }, - key, - fdata.data - ).then(decrypted => { - this.emit('decrypting', false); - return Promise.resolve(decrypted) - }), - fdata.filename, - hexToArray(fdata.aad) - ]); - }).then(([decrypted, fname, proposedHash]) => { - this.emit('hashing', true); - return window.crypto.subtle.digest('SHA-256', decrypted).then(calculatedHash => { - this.emit('hashing', false); - const integrity = new Uint8Array(calculatedHash).toString() === proposedHash.toString(); - if (!integrity) { - this.emit('unsafe', true) - return Promise.reject(); - } else { - this.emit('safe', true); - return Promise.all([ - decrypted, - decodeURIComponent(fname) - ]); - } + ]) + .then(([fdata, key]) => { + this.emit('decrypting', true); + return Promise.all([ + window.crypto.subtle + .decrypt( + { + name: 'AES-GCM', + iv: hexToArray(fdata.iv), + additionalData: hexToArray(fdata.aad), + tagLength: 128 + }, + key, + fdata.data + ) + .then(decrypted => { + this.emit('decrypting', false); + return Promise.resolve(decrypted); + }), + fdata.filename, + hexToArray(fdata.aad) + ]); }) - }) + .then(([decrypted, fname, proposedHash]) => { + this.emit('hashing', true); + return window.crypto.subtle + .digest('SHA-256', decrypted) + .then(calculatedHash => { + this.emit('hashing', false); + const integrity = + new Uint8Array(calculatedHash).toString() === + proposedHash.toString(); + if (!integrity) { + this.emit('unsafe', true); + return Promise.reject(); + } else { + this.emit('safe', true); + return Promise.all([decrypted, decodeURIComponent(fname)]); + } + }); + }); } } diff --git a/frontend/src/fileSender.js b/frontend/src/fileSender.js index a16b1849..97c76deb 100644 --- a/frontend/src/fileSender.js +++ b/frontend/src/fileSender.js @@ -118,14 +118,16 @@ class FileSender extends EventEmitter { xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { - // uuid field and url field - const responseObj = JSON.parse(xhr.responseText); - resolve({ - url: responseObj.url, - fileId: responseObj.id, - secretKey: keydata.k, - deleteToken: responseObj.delete - }); + if (xhr.status === 200) { + const responseObj = JSON.parse(xhr.responseText); + return resolve({ + url: responseObj.url, + fileId: responseObj.id, + secretKey: keydata.k, + deleteToken: responseObj.delete + }); + } + reject(xhr.status); } }; diff --git a/frontend/src/main.js b/frontend/src/main.js deleted file mode 100644 index 12c05a38..00000000 --- a/frontend/src/main.js +++ /dev/null @@ -1,5 +0,0 @@ -window.Raven = require('raven-js'); -window.Raven.config(window.dsn).install(); -window.dsn = undefined; -require('./upload'); -require('./download'); diff --git a/frontend/src/storage.js b/frontend/src/storage.js new file mode 100644 index 00000000..c95d93a6 --- /dev/null +++ b/frontend/src/storage.js @@ -0,0 +1,66 @@ +const { isFile } = require('./utils'); + +class Storage { + constructor(engine) { + this.engine = engine; + } + + get totalDownloads() { + return Number(this.engine.getItem('totalDownloads')); + } + set totalDownloads(n) { + this.engine.setItem('totalDownloads', n); + } + get totalUploads() { + return Number(this.engine.getItem('totalUploads')); + } + set totalUploads(n) { + this.engine.setItem('totalUploads', n); + } + get referrer() { + return this.engine.getItem('referrer'); + } + set referrer(str) { + this.engine.setItem('referrer', str); + } + + get files() { + const fs = []; + for (let i = 0; i < this.engine.length; i++) { + const k = this.engine.key(i); + if (isFile(k)) { + fs.push(JSON.parse(this.engine.getItem(k))); // parse or whatever else + } + } + return fs; + } + + get numFiles() { + let length = 0; + for (let i = 0; i < this.engine.length; i++) { + const k = this.engine.key(i); + if (isFile(k)) { + length += 1; + } + } + return length; + } + + getFileById(id) { + return this.engine.getItem(id); + } + + has(property) { + return this.engine.hasOwnProperty(property); + } + + remove(property) { + this.engine.removeItem(property); + } + + addFile(id, file) { + this.engine.setItem(id, JSON.stringify(file)); + } +} + +module.exports = Storage; diff --git a/frontend/src/upload.js b/frontend/src/upload.js index 82f6c55b..84279760 100644 --- a/frontend/src/upload.js +++ b/frontend/src/upload.js @@ -1,17 +1,74 @@ +/* global MAXFILESIZE EXPIRE_SECONDS */ +require('./common'); const FileSender = require('./fileSender'); -const { notify, gcmCompliant } = require('./utils'); +const { + notify, + gcmCompliant, + findMetric, + sendEvent, + ONE_DAY_IN_MS +} = require('./utils'); +const bytes = require('bytes'); +const Storage = require('./storage'); +const storage = new Storage(localStorage); + const $ = require('jquery'); require('jquery-circle-progress'); const Raven = window.Raven; +if (storage.has('referrer')) { + window.referrer = storage.referrer; + storage.remove('referrer'); +} else { + window.referrer = 'external'; +} + $(document).ready(function() { gcmCompliant().catch(err => { $('#page-one').attr('hidden', true); - $('#unsupported-browser').removeAttr('hidden'); + sendEvent('sender', 'unsupported', { + cd6: err + }).then(() => { + location.replace('/unsupported'); + }); }); $('#file-upload').change(onUpload); + + $('.legal-links a, .social-links a, #dl-firefox').click(function(target) { + target.preventDefault(); + const metric = findMetric(target.currentTarget.href); + // record exited event by recipient + sendEvent('sender', 'exited', { + cd3: metric + }).then(() => { + location.href = target.currentTarget.href; + }); + }); + + $('#send-new-completed').click(function(target) { + target.preventDefault(); + // record restarted event + sendEvent('sender', 'restarted', { + cd2: 'completed' + }).then(() => { + storage.referrer = 'completed-upload'; + location.href = target.currentTarget.href; + }); + }); + + $('#send-new-error').click(function(target) { + target.preventDefault(); + // record restarted event + sendEvent('sender', 'restarted', { + cd2: 'errored' + }).then(() => { + storage.referrer = 'errored-upload'; + location.href = target.currentTarget.href; + }); + }); + $('body').on('dragover', allowDrop).on('drop', onUpload); // reset copy button const $copyBtn = $('#copy-btn'); @@ -19,18 +76,23 @@ $(document).ready(function() { $('#link').attr('disabled', false); $copyBtn.attr('data-l10n-id', 'copyUrlFormButton'); - if (localStorage.length === 0) { + const files = storage.files; + if (files.length === 0) { toggleHeader(); } else { - for (let i = 0; i < localStorage.length; i++) { - const id = localStorage.key(i); - //check if file exists before adding to list - checkExistence(id, true); + for (const index in files) { + const id = files[index].fileId; + //check if file still exists before adding to list + checkExistence(id, files[index], true); } } // copy link to clipboard $copyBtn.click(() => { + // record copied event from success screen + sendEvent('sender', 'copied', { + cd4: 'success-screen' + }); const aux = document.createElement('input'); aux.setAttribute('value', $('#link').attr('value')); document.body.appendChild(aux); @@ -40,7 +102,9 @@ $(document).ready(function() { //disable button for 3s $copyBtn.attr('disabled', true); $('#link').attr('disabled', true); - $copyBtn.html(''); + $copyBtn.html( + '' + ); window.setTimeout(() => { $copyBtn.attr('disabled', false); $('#link').attr('disabled', false); @@ -69,14 +133,28 @@ $(document).ready(function() { // on file upload by browse or drag & drop function onUpload(event) { event.preventDefault(); + + // don't allow upload if not on upload page + if ($('#page-one').attr('hidden')){ + return; + } + + storage.totalUploads += 1; + let file = ''; if (event.type === 'drop') { - if (event.originalEvent.dataTransfer.files.length > 1 || event.originalEvent.dataTransfer.files[0].size === 0){ + if (!event.originalEvent.dataTransfer.files[0]) { $('.upload-window').removeClass('ondrag'); - document.l10n.formatValue('uploadPageMultipleFilesAlert') - .then(str => { - alert(str); - }); + return; + } + if ( + event.originalEvent.dataTransfer.files.length > 1 || + event.originalEvent.dataTransfer.files[0].size === 0 + ) { + $('.upload-window').removeClass('ondrag'); + document.l10n.formatValue('uploadPageMultipleFilesAlert').then(str => { + alert(str); + }); return; } file = event.originalEvent.dataTransfer.files[0]; @@ -84,21 +162,39 @@ $(document).ready(function() { file = event.target.files[0]; } + if (file.size > MAXFILESIZE) { + return document.l10n + .formatValue('fileTooBig', { size: bytes(MAXFILESIZE) }) + .then(alert); + } + $('#page-one').attr('hidden', true); $('#upload-error').attr('hidden', true); $('#upload-progress').removeAttr('hidden'); + document.l10n.formatValue('importingFile').then(importingFile => { + $('.progress-text').text(importingFile); + }); //don't allow drag and drop when not on page-one $('body').off('drop', onUpload); - const expiration = 24 * 60 * 60 * 1000; //will eventually come from a field const fileSender = new FileSender(file); $('#cancel-upload').click(() => { fileSender.cancel(); location.reload(); - document.l10n.formatValue('uploadCancelNotification') - .then(str => { - notify(str); - }); + document.l10n.formatValue('uploadCancelNotification').then(str => { + notify(str); + }); + storage.referrer = 'cancelled-upload'; + + // record upload-stopped (cancelled) by sender + sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'cancelled' + }); }); fileSender.on('progress', progress => { @@ -106,101 +202,152 @@ $(document).ready(function() { // update progress bar $('#ul-progress').circleProgress('value', percent); $('#ul-progress').circleProgress().on('circle-animation-end', function() { - $('.percent-number').html(`${Math.floor(percent * 100)}`); + $('.percent-number').text(`${Math.floor(percent * 100)}`); }); - if (progress[1] < 1000000) { - $('.progress-text').text( - `${file.name} (${(progress[0] / 1000).toFixed(1)}KB of ${(progress[1] / 1000).toFixed(1)}KB)` - ); - } else if (progress[1] < 1000000000) { - $('.progress-text').text( - `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000).toFixed(1)}MB)` - ); - } else { - $('.progress-text').text( - `${file.name} (${(progress[0] / 1000000).toFixed(1)}MB of ${(progress[1] / 1000000000).toFixed(1)}GB)` - ); - } - }); - - fileSender.on('loading', isStillLoading => { - // The file is loading into Firefox at this stage - if (isStillLoading) { - console.log('Processing'); - } else { - console.log('Finished processing'); - } + $('.progress-text').text( + `${file.name} (${bytes(progress[0], { + decimalPlaces: 1, + fixedDecimals: true + })} of ${bytes(progress[1], { decimalPlaces: 1 })})` + ); }); fileSender.on('hashing', isStillHashing => { // The file is being hashed if (isStillHashing) { - console.log('Hashing'); + document.l10n.formatValue('verifyingFile').then(verifyingFile => { + $('.progress-text').text(verifyingFile); + }); } else { console.log('Finished hashing'); } }); + let uploadStart; fileSender.on('encrypting', isStillEncrypting => { // The file is being encrypted if (isStillEncrypting) { - console.log('Encrypting'); + document.l10n.formatValue('encryptingFile').then(encryptingFile => { + $('.progress-text').text(encryptingFile); + }); } else { console.log('Finished encrypting'); + uploadStart = Date.now(); } }); - let t = ''; - fileSender - .upload() - .then(info => { - const fileData = { - name: file.name, - fileId: info.fileId, - url: info.url, - secretKey: info.secretKey, - deleteToken: info.deleteToken, - creationDate: new Date(), - expiry: expiration - }; - localStorage.setItem(info.fileId, JSON.stringify(fileData)); - $('#upload-filename').attr('data-l10n-id', 'uploadSuccessConfirmHeader'); - t = window.setTimeout(() => { + + let t; + const startTime = Date.now(); + const unexpiredFiles = storage.numFiles + 1; + + // record upload-started event by sender + sendEvent('sender', 'upload-started', { + cm1: file.size, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd5: window.referrer + }); + + // For large files we need to give the ui a tick to breathe and update + // before we kick off the FileSender + setTimeout(() => { + fileSender + .upload() + .then(info => { + const endTime = Date.now(); + const totalTime = endTime - startTime; + const uploadTime = endTime - uploadStart; + const uploadSpeed = file.size / (uploadTime / 1000); + const expiration = EXPIRE_SECONDS * 1000; + + // record upload-stopped (completed) by sender + sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm2: totalTime, + cm3: uploadSpeed, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'completed' + }); + + const fileData = { + name: file.name, + size: file.size, + fileId: info.fileId, + url: info.url, + secretKey: info.secretKey, + deleteToken: info.deleteToken, + creationDate: new Date(), + expiry: expiration, + totalTime: totalTime, + typeOfUpload: event.type === 'drop' ? 'drop' : 'click', + uploadSpeed: uploadSpeed + }; + + storage.addFile(info.fileId, fileData); + $('#upload-filename').attr( + 'data-l10n-id', + 'uploadSuccessConfirmHeader' + ); + t = window.setTimeout(() => { + $('#page-one').attr('hidden', true); + $('#upload-progress').attr('hidden', true); + $('#upload-error').attr('hidden', true); + $('#share-link').removeAttr('hidden'); + }, 1000); + + populateFileList(fileData); + document.l10n.formatValue('notifyUploadDone').then(str => { + notify(str); + }); + }) + .catch(err => { + // err is 0 when coming from a cancel upload event + if (err === 0) { + return; + } + // only show error page when the error is anything other than user cancelling the upload + Raven.captureException(err); $('#page-one').attr('hidden', true); $('#upload-progress').attr('hidden', true); - $('#upload-error').attr('hidden', true); - $('#share-link').removeAttr('hidden'); - }, 1000); + $('#upload-error').removeAttr('hidden'); + window.clearTimeout(t); - populateFileList(JSON.stringify(fileData)); - document.l10n.formatValue('notifyUploadDone') - .then(str => { - notify(str); - }); - }) - .catch(err => { - Raven.captureException(err); - console.log(err); - $('#page-one').attr('hidden', true); - $('#upload-progress').attr('hidden', true); - $('#upload-error').removeAttr('hidden'); - window.clearTimeout(t); - }); + // record upload-stopped (errored) by sender + sendEvent('sender', 'upload-stopped', { + cm1: file.size, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: event.type === 'drop' ? 'drop' : 'click', + cd2: 'errored', + cd6: err + }); + }); + }, 10); } function allowDrop(ev) { ev.preventDefault(); } - function checkExistence(id, populate) { + function checkExistence(id, file, populate) { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { if (populate) { - populateFileList(localStorage.getItem(id)); + populateFileList(file); } } else if (xhr.status === 404) { - localStorage.removeItem(id); + storage.remove(id); + if (storage.numFiles === 0) { + toggleHeader(); + } } } }; @@ -208,21 +355,23 @@ $(document).ready(function() { xhr.send(); } - //update file table with current files in localStorage + //update file table with current files in storage function populateFileList(file) { - try { - file = JSON.parse(file); - } catch (e) { - return; - } - const row = document.createElement('tr'); const name = document.createElement('td'); const link = document.createElement('td'); - const $copyIcon = $('', { src: '/resources/copy-16.svg', class: 'icon-copy', 'data-l10n-id': 'copyUrlHover'}); + const $copyIcon = $('', { + src: '/resources/copy-16.svg', + class: 'icon-copy', + 'data-l10n-id': 'copyUrlHover' + }); const expiry = document.createElement('td'); const del = document.createElement('td'); - const $delIcon = $('', { src: '/resources/close-16.svg', class: 'icon-delete', 'data-l10n-id': 'deleteButtonHover' }); + const $delIcon = $('', { + src: '/resources/close-16.svg', + class: 'icon-delete', + 'data-l10n-id': 'deleteButtonHover' + }); const popupDiv = document.createElement('div'); const $popupText = $('
', { class: 'popuptext' }); const cellText = document.createTextNode(file.name); @@ -230,14 +379,8 @@ $(document).ready(function() { const url = file.url.trim() + `#${file.secretKey}`.trim(); $('#link').attr('value', url); - $('#copy-text').attr( - 'data-l10n-args', - '{"filename": "' + file.name + '"}' - ); - $('#copy-text').attr( - 'data-l10n-id', - 'copyUrlFormLabelWithName' - ); + $('#copy-text').attr('data-l10n-args', '{"filename": "' + file.name + '"}'); + $('#copy-text').attr('data-l10n-id', 'copyUrlFormLabelWithName'); $popupText.attr('tabindex', '-1'); name.appendChild(cellText); @@ -258,16 +401,19 @@ $(document).ready(function() { //copy link to clipboard when icon clicked $copyIcon.click(function() { + // record copied event from upload list + sendEvent('sender', 'copied', { + cd4: 'upload-list' + }); const aux = document.createElement('input'); aux.setAttribute('value', url); document.body.appendChild(aux); aux.select(); document.execCommand('copy'); document.body.removeChild(aux); - document.l10n.formatValue('copiedUrl') - .then(translated => { - link.innerHTML = translated; - }); + document.l10n.formatValue('copiedUrl').then(translated => { + link.innerHTML = translated; + }); window.setTimeout(() => { const linkImg = document.createElement('img'); $(linkImg).addClass('icon-copy'); @@ -283,7 +429,7 @@ $(document).ready(function() { future.setTime(file.creationDate.getTime() + file.expiry); let countdown = 0; - countdown = future.getTime() - new Date().getTime(); + countdown = future.getTime() - Date.now(); let minutes = Math.floor(countdown / 1000 / 60); let hours = Math.floor(minutes / 60); let seconds = Math.floor(countdown / 1000 % 60); @@ -291,7 +437,7 @@ $(document).ready(function() { poll(); function poll() { - countdown = future.getTime() - new Date().getTime(); + countdown = future.getTime() - Date.now(); minutes = Math.floor(countdown / 1000 / 60); hours = Math.floor(minutes / 60); seconds = Math.floor(countdown / 1000 % 60); @@ -310,7 +456,7 @@ $(document).ready(function() { } //remove from list when expired if (countdown <= 0) { - localStorage.removeItem(file.fileId); + storage.remove(file.fileId); $(expiry).parents('tr').remove(); window.clearTimeout(t); toggleHeader(); @@ -319,20 +465,14 @@ $(document).ready(function() { // create popup popupDiv.classList.add('popup'); - const popupDelSpan = document.createElement('span'); - $(popupDelSpan).addClass('del-file'); - $(popupDelSpan).attr('data-l10n-id', 'deleteFileList'); - - const popupNvmSpan = document.createElement('span'); - $(popupNvmSpan).addClass('nvm'); - $(popupNvmSpan).attr('data-l10n-id', 'nevermindButton'); - - $popupText.html([ - popupDelSpan, - '
', - popupNvmSpan - ]); + const $popupMessage = $('
', { class: 'popup-message' }); + $popupMessage.attr('data-l10n-id', 'deletePopupText'); + const $popupDelSpan = $('', { class: 'popup-yes' }); + $popupDelSpan.attr('data-l10n-id', 'deletePopupYes'); + const $popupNvmSpan = $('', { class: 'popup-no' }); + $popupNvmSpan.attr('data-l10n-id', 'deletePopupCancel'); + $popupText.html([$popupMessage, $popupDelSpan, $popupNvmSpan]); // add data cells to table row row.appendChild(name); @@ -345,18 +485,51 @@ $(document).ready(function() { row.appendChild(del); $('tbody').append(row); //add row to table + const unexpiredFiles = storage.numFiles; + // delete file - $popupText.find('.del-file').click(e => { + $popupText.find('.popup-yes').click(e => { FileSender.delete(file.fileId, file.deleteToken).then(() => { $(e.target).parents('tr').remove(); - localStorage.removeItem(file.fileId); + const timeToExpiry = + ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); + // record upload-deleted from file list + sendEvent('sender', 'upload-deleted', { + cm1: file.size, + cm2: file.totalTime, + cm3: file.uploadSpeed, + cm4: timeToExpiry, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: file.typeOfUpload, + cd4: 'upload-list' + }).then(() => { + storage.remove(file.fileId); + }); toggleHeader(); }); }); + document.getElementById('delete-file').onclick = () => { FileSender.delete(file.fileId, file.deleteToken).then(() => { - localStorage.removeItem(file.fileId); - location.reload(); + const timeToExpiry = + ONE_DAY_IN_MS - (Date.now() - file.creationDate.getTime()); + // record upload-deleted from success screen + sendEvent('sender', 'upload-deleted', { + cm1: file.size, + cm2: file.totalTime, + cm3: file.uploadSpeed, + cm4: timeToExpiry, + cm5: storage.totalUploads, + cm6: unexpiredFiles, + cm7: storage.totalDownloads, + cd1: file.typeOfUpload, + cd4: 'success-screen' + }).then(() => { + storage.remove(file.fileId); + location.reload(); + }); }); }; // show popup @@ -365,7 +538,7 @@ $(document).ready(function() { $popupText.focus(); }); // hide popup - $popupText.find('.nvm').click(function(e) { + $popupText.find('.popup-no').click(function(e) { e.stopPropagation(); $popupText.removeClass('show'); }); @@ -377,7 +550,6 @@ $(document).ready(function() { $popupText.removeClass('show'); }); - toggleHeader(); } function toggleHeader() { diff --git a/frontend/src/utils.js b/frontend/src/utils.js index aff37488..d338b7c9 100644 --- a/frontend/src/utils.js +++ b/frontend/src/utils.js @@ -69,9 +69,55 @@ function gcmCompliant() { } } +function findMetric(href) { + switch (href) { + case 'https://www.mozilla.org/': + return 'mozilla'; + case 'https://www.mozilla.org/about/legal': + return 'legal'; + case 'https://testpilot.firefox.com/about': + return 'about'; + case 'https://testpilot.firefox.com/privacy': + return 'privacy'; + case 'https://testpilot.firefox.com/terms': + return 'terms'; + case 'https://www.mozilla.org/en-US/privacy/websites/#cookies': + return 'cookies'; + case 'https://github.com/mozilla/send': + return 'github'; + case 'https://twitter.com/FxTestPilot': + return 'twitter'; + case 'https://www.mozilla.org/firefox/new/?scene=2': + return 'download-firefox'; + default: + return 'other'; + } +} + +function isFile(id) { + return ![ + 'referrer', + 'totalDownloads', + 'totalUploads', + 'testpilot_ga__cid' + ].includes(id); +} + +function sendEvent() { + return window.analytics.sendEvent + .apply(window.analytics, arguments) + .catch(() => 0); +} + +const ONE_DAY_IN_MS = 86400000; + module.exports = { arrayToHex, hexToArray, notify, - gcmCompliant + gcmCompliant, + findMetric, + isFile, + sendEvent, + ONE_DAY_IN_MS }; diff --git a/package-lock.json b/package-lock.json index 8e2466bd..e3cd7b39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "firefox-send", - "version": "0.1.2", + "version": "0.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -36,18 +36,15 @@ } } }, - "adm-zip": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.7.tgz", - "integrity": "sha1-hgbCy/HEJs6MjsABdER/1Jtur8E=" - }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", "dev": true, "requires": { "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", "json-stable-stringify": "1.0.1" }, "dependencies": { @@ -99,16 +96,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, - "anymatch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", - "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=", - "dev": true, - "requires": { - "arrify": "1.0.1", - "micromatch": "2.3.11" - } - }, "argparse": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", @@ -124,13 +111,13 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.0.3" + "arr-flatten": "1.1.0" } }, "arr-flatten": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", - "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "array-differ": { @@ -196,9 +183,9 @@ "dev": true }, "asap": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.5.tgz", - "integrity": "sha1-UidltQw1EEkOUtfc/ghe+bqWlY8=" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1.js": { "version": "4.9.1", @@ -234,16 +221,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "autoprefixer": { "version": "6.7.7", @@ -252,7 +234,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000693", + "caniuse-db": "1.0.30000704", "normalize-range": "0.1.2", "num2fraction": "1.2.2", "postcss": "5.2.17", @@ -260,12 +242,13 @@ } }, "aws-sdk": { - "version": "2.77.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.77.0.tgz", - "integrity": "sha1-gJCQu4dNj0//ysUxZilYdjjnhlw=", + "version": "2.89.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.89.0.tgz", + "integrity": "sha1-2/RpmcvV50zhsGZAsH+o4ljvB7w=", "requires": { - "buffer": "5.0.6", + "buffer": "4.9.1", "crypto-browserify": "1.0.9", + "events": "1.1.1", "jmespath": "0.15.0", "querystring": "0.2.0", "sax": "1.2.1", @@ -283,7 +266,7 @@ "requires": { "chalk": "1.1.3", "esutils": "2.0.2", - "js-tokens": "3.0.1" + "js-tokens": "3.0.2" } }, "balanced-match": { @@ -296,12 +279,6 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=" }, - "binary-extensions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", - "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=", - "dev": true - }, "bn.js": { "version": "4.11.7", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.7.tgz", @@ -409,7 +386,7 @@ "concat-stream": "1.5.2", "console-browserify": "1.1.0", "constants-browserify": "1.0.0", - "crypto-browserify": "3.11.0", + "crypto-browserify": "3.11.1", "defined": "1.0.0", "deps-sort": "2.0.0", "domain-browser": "1.1.7", @@ -431,7 +408,7 @@ "punycode": "1.3.2", "querystring-es3": "0.2.1", "read-only-stream": "2.0.0", - "readable-stream": "2.3.2", + "readable-stream": "2.3.3", "resolve": "1.3.3", "shasum": "1.0.2", "shell-quote": "1.6.1", @@ -449,10 +426,20 @@ "xtend": "4.0.1" }, "dependencies": { + "buffer": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", + "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", + "dev": true, + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, "crypto-browserify": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", - "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=", + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", "dev": true, "requires": { "browserify-cipher": "1.0.0", @@ -481,16 +468,10 @@ "path-is-absolute": "1.0.1" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -530,7 +511,7 @@ "dev": true, "requires": { "buffer-xor": "1.0.3", - "cipher-base": "1.0.3", + "cipher-base": "1.0.4", "create-hash": "1.1.3", "evp_bytestokey": "1.0.0", "inherits": "2.0.3" @@ -553,7 +534,7 @@ "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", "dev": true, "requires": { - "cipher-base": "1.0.3", + "cipher-base": "1.0.4", "des.js": "1.0.0", "inherits": "2.0.3" } @@ -598,17 +579,18 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000693", - "electron-to-chromium": "1.3.14" + "caniuse-db": "1.0.30000704", + "electron-to-chromium": "1.3.16" } }, "buffer": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.6.tgz", - "integrity": "sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg=", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "requires": { "base64-js": "1.2.1", - "ieee754": "1.1.8" + "ieee754": "1.1.8", + "isarray": "1.0.0" } }, "buffer-xor": { @@ -693,9 +675,9 @@ "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, "caniuse-db": { - "version": "1.0.30000693", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000693.tgz", - "integrity": "sha1-hRDnqasErcyiOl3O+jTfnSjBziA=", + "version": "1.0.30000704", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000704.tgz", + "integrity": "sha1-jFqm/tgFjmXHDywfXWP3CIZQcFw=", "dev": true }, "center-align": { @@ -719,30 +701,14 @@ "supports-color": "2.0.0" } }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.0", - "async-each": "1.0.1", - "fsevents": "1.1.2", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, "cipher-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", - "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "2.0.3" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "circular-json": { @@ -799,12 +765,27 @@ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, "color-diff": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/color-diff/-/color-diff-0.1.7.tgz", "integrity": "sha1-bbeM2UgqjkWdQIIer0tQMoPcuOI=", "dev": true }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "colorguard": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/colorguard/-/colorguard-1.2.0.tgz", @@ -867,6 +848,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, "requires": { "delayed-stream": "1.0.0" } @@ -875,6 +857,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, "requires": { "graceful-readlink": "1.0.1" } @@ -882,7 +865,8 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true }, "concat-map": { "version": "0.0.1", @@ -900,12 +884,6 @@ "typedarray": "0.0.6" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", @@ -1064,7 +1042,14 @@ "cookiejar": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", - "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=" + "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", + "dev": true + }, + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -1072,13 +1057,13 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.1.3.tgz", - "integrity": "sha1-lSdx6w3dwcs/ovb75RpSLpOz7go=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.1.tgz", + "integrity": "sha512-17m9pl5cD9jhPUHqaxSA4fyoiAQJUG7V3CQDxCF7gWzGYeUY0YEnLQdQyOEKjEPVv0yGbdCfdfJMq6SphRiRjw==", "dev": true, "requires": { "is-directory": "0.3.1", - "js-yaml": "3.8.4", + "js-yaml": "3.9.0", "minimist": "1.2.0", "object-assign": "4.1.1", "os-homedir": "1.0.2", @@ -1110,7 +1095,7 @@ "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", "dev": true, "requires": { - "cipher-base": "1.0.3", + "cipher-base": "1.0.4", "inherits": "2.0.3", "ripemd160": "2.0.1", "sha.js": "2.4.8" @@ -1122,7 +1107,7 @@ "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", "dev": true, "requires": { - "cipher-base": "1.0.3", + "cipher-base": "1.0.4", "create-hash": "1.1.3", "inherits": "2.0.3", "ripemd160": "2.0.1", @@ -1130,19 +1115,11 @@ "sha.js": "2.4.8" } }, - "cross-env": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.0.1.tgz", - "integrity": "sha1-/05y6kO0faJIa0On8gQ7JgnkSRM=", - "requires": { - "cross-spawn": "5.1.0", - "is-windows": "1.0.1" - } - }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, "requires": { "lru-cache": "4.1.1", "shebang-command": "1.2.0", @@ -1181,6 +1158,12 @@ "readable-stream": "1.1.14" } }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "through2": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", @@ -1299,7 +1282,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "depd": { "version": "1.1.0", @@ -1382,14 +1366,6 @@ "requires": { "esutils": "2.0.2", "isarray": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } } }, "doiuse": { @@ -1399,7 +1375,7 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000693", + "caniuse-db": "1.0.30000704", "css-rule-stream": "1.1.0", "duplexer2": "0.0.2", "jsonfilter": "1.1.2", @@ -1421,6 +1397,12 @@ "readable-stream": "1.1.14" } }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "through2": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", @@ -1475,19 +1457,13 @@ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, "requires": { - "readable-stream": "2.3.2" + "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -1516,9 +1492,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz", - "integrity": "sha1-ZK8Pnv08PGrNV9cfg7Scp+6cS0M=", + "version": "1.3.16", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz", + "integrity": "sha1-0OAmc1dUdwkBrjAaIWZMukXZL30=", "dev": true }, "elliptic": { @@ -1529,7 +1505,7 @@ "requires": { "bn.js": "4.11.7", "brorand": "1.1.0", - "hash.js": "1.1.1", + "hash.js": "1.1.3", "hmac-drbg": "1.0.1", "inherits": "2.0.3", "minimalistic-assert": "1.0.0", @@ -1573,6 +1549,12 @@ "is-symbol": "1.0.1" } }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1584,14 +1566,16 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.1.0.tgz", - "integrity": "sha1-u7VaKCIO4Itp2pVU1FprLr/X2RM=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.3.0.tgz", + "integrity": "sha1-/NfJY3a780yF7mftABKimWQrEI8=", "dev": true, "requires": { + "ajv": "5.2.2", "babel-code-frame": "6.22.0", "chalk": "1.1.3", "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", "debug": "2.6.8", "doctrine": "2.0.0", "eslint-scope": "3.7.1", @@ -1600,14 +1584,14 @@ "estraverse": "4.2.0", "esutils": "2.0.2", "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", "glob": "7.1.2", "globals": "9.18.0", "ignore": "3.3.3", "imurmurhash": "0.1.4", - "inquirer": "3.1.1", - "is-my-json-valid": "2.16.0", + "inquirer": "3.2.0", "is-resolvable": "1.0.0", - "js-yaml": "3.8.4", + "js-yaml": "3.9.0", "json-stable-stringify": "1.0.1", "levn": "0.3.0", "lodash": "4.17.4", @@ -1619,6 +1603,7 @@ "pluralize": "4.0.0", "progress": "2.0.0", "require-uncached": "1.0.3", + "semver": "5.3.0", "strip-json-comments": "2.0.1", "table": "4.0.1", "text-table": "0.2.0" @@ -1631,7 +1616,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.2", + "readable-stream": "2.3.3", "typedarray": "0.0.6" } }, @@ -1647,7 +1632,7 @@ "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -1658,12 +1643,6 @@ "path-is-absolute": "1.0.1" } }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "json-stable-stringify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", @@ -1674,9 +1653,9 @@ } }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -1691,7 +1670,7 @@ "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, "requires": { "safe-buffer": "5.1.1" @@ -1709,9 +1688,9 @@ } }, "eslint-plugin-node": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.0.0.tgz", - "integrity": "sha512-9xERRx9V/8ciUHlTDlz9S4JiTL6Dc5oO+jKTy2mvQpxjhycpYZXzTT1t90IXjf+nAYw6/8sDnZfkeixJHxromA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-5.1.1.tgz", + "integrity": "sha512-3xdoEbPyyQNyGhhqttjgSO3cU/non8QDBJF8ttGaHM2h8CaY5zFIngtqW6ZbLEIvhpoFPDVwiQg61b8zanx5zQ==", "dev": true, "requires": { "ignore": "3.3.3", @@ -1745,22 +1724,22 @@ "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=", "dev": true, "requires": { - "acorn": "5.0.3", + "acorn": "5.1.1", "acorn-jsx": "3.0.1" }, "dependencies": { "acorn": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", - "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.1.tgz", + "integrity": "sha512-vOk6uEMctu0vQrvuSqFdJyqj1Q0S5VTDL79qtjo+DhRr+1mmaD+tluFSCZqhvi/JUhXSzoZN2BhtstaPEeE8cw==", "dev": true } } }, "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", "dev": true }, "esquery": { @@ -1817,8 +1796,7 @@ "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, "evp_bytestokey": { "version": "1.0.0", @@ -1911,7 +1889,8 @@ "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true }, "external-editor": { "version": "2.0.4", @@ -1920,7 +1899,7 @@ "dev": true, "requires": { "iconv-lite": "0.4.18", - "jschardet": "1.4.2", + "jschardet": "1.5.0", "tmp": "0.0.31" }, "dependencies": { @@ -1941,6 +1920,12 @@ "is-extglob": "1.0.0" } }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -2040,12 +2025,14 @@ "fluent": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.3.1.tgz", - "integrity": "sha1-f6TOw/9X1wjI4me3sMSjTXog7nI=" + "integrity": "sha1-f6TOw/9X1wjI4me3sMSjTXog7nI=", + "dev": true }, "fluent-intl-polyfill": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/fluent-intl-polyfill/-/fluent-intl-polyfill-0.1.0.tgz", "integrity": "sha1-ETOUSrJHeINHOZVZaIPg05z4hc8=", + "dev": true, "requires": { "intl-pluralrules": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b" } @@ -2053,7 +2040,8 @@ "fluent-langneg": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/fluent-langneg/-/fluent-langneg-0.0.2.tgz", - "integrity": "sha1-1cZuKvEV70f3TQYn7UawrvbLe2w=" + "integrity": "sha1-1cZuKvEV70f3TQYn7UawrvbLe2w=", + "dev": true }, "for-in": { "version": "1.0.2", @@ -2079,6 +2067,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.2.0.tgz", "integrity": "sha1-ml47kpX5gLJiPPZPojixTOvKcHs=", + "dev": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.5", @@ -2097,7 +2086,8 @@ "formidable": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", - "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=" + "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=", + "dev": true }, "forwarded": { "version": "0.1.0", @@ -2123,1046 +2113,26 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", - "integrity": "sha1-MoK3E/s62A7eDp/PRhG1qm/AM/Q=", - "dev": true, - "optional": true, - "requires": { - "nan": "2.6.2", - "node-pre-gyp": "0.6.36" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "aproba": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.36", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", - "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", - "dev": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", - "dev": true - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "function-bind": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gather-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gather-stream/-/gather-stream-1.0.0.tgz", "integrity": "sha1-szmUr0V6gRVwDUEPMXczy+egkEs=", "dev": true }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "1.0.2" - } - }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", @@ -3261,7 +2231,8 @@ "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true }, "growl": { "version": "1.9.2", @@ -3278,27 +2249,6 @@ "optimist": "0.6.1", "source-map": "0.4.4", "uglify-js": "2.8.29" - }, - "dependencies": { - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "optional": true, - "requires": { - "source-map": "0.5.6", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", - "optional": true - } - } - } } }, "has": { @@ -3319,9 +2269,9 @@ } }, "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, "hash-base": { @@ -3334,9 +2284,9 @@ } }, "hash.js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.1.tgz", - "integrity": "sha512-I2TYCUjYQMmqmRMCp6jKMC5bvdXxGIZ/heITRR/0F1u0OP920ImEj/cXt3WgcTKBnNYGn7enxUzdai3db829JA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", "dev": true, "requires": { "inherits": "2.0.3", @@ -3344,19 +2294,19 @@ } }, "helmet": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.6.1.tgz", - "integrity": "sha1-kfOqf6TJRnFZX7Vo39jChImjiL4=", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.8.0.tgz", + "integrity": "sha512-66RfO0koYNDIX94d8iXc0Fg+EfuQ2fvDr104bTiCno3RyD+igovNOFSe9F/X3ROrcCceLYQgxRSFD5P7fSveuA==", "requires": { "connect": "3.6.2", "dns-prefetch-control": "0.1.0", "dont-sniff-mimetype": "1.0.0", "expect-ct": "0.1.0", "frameguard": "3.0.0", - "helmet-csp": "2.4.0", + "helmet-csp": "2.5.0", "hide-powered-by": "1.0.0", "hpkp": "2.0.0", - "hsts": "2.0.0", + "hsts": "2.1.0", "ienoopen": "1.0.0", "nocache": "2.0.0", "referrer-policy": "1.1.0", @@ -3364,15 +2314,15 @@ } }, "helmet-csp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.4.0.tgz", - "integrity": "sha1-flOhVxZ6BkWq3XF30SrmxgXBhC4=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.5.0.tgz", + "integrity": "sha512-KdGChwze2cJke00PYAg75ztUqMoC8N5ez/Exz183xHsAT5MAHqQR1A5YnivBxkISDnT+fD38jtEEat1aH5QfYw==", "requires": { "camelize": "1.0.0", "content-security-policy-builder": "1.1.0", "dasherize": "2.0.0", "lodash.reduce": "4.6.0", - "platform": "1.3.3" + "platform": "1.3.4" } }, "hide-powered-by": { @@ -3386,15 +2336,15 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "1.1.1", + "hash.js": "1.1.3", "minimalistic-assert": "1.0.0", "minimalistic-crypto-utils": "1.0.1" } }, "hosted-git-info": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", - "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", + "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, "hpkp": { @@ -3403,17 +2353,14 @@ "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" }, "hsts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.0.0.tgz", - "integrity": "sha1-pSI0xgcN7PIUsra3C7FE0H5Hdsc=", - "requires": { - "core-util-is": "1.0.2" - } + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz", + "integrity": "sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA==" }, "html-tags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz", - "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", "dev": true }, "htmlescape": { @@ -3460,6 +2407,12 @@ "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3519,13 +2472,13 @@ } }, "inquirer": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.1.1.tgz", - "integrity": "sha512-H50sHQwgvvaTBd3HpKMVtL/u6LoHDvYym51gd7bGQe/+9HkCE+J0/3N5FJLfd6O6oz44hHewC2Pc2LodzWVafQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.2.0.tgz", + "integrity": "sha512-4CyUYMP7lOBkiUU1rR24WGrfRX6SucwbY2Mqb1PdApU24wnTIk4TsnkQwV72dDdIKZ2ycLP+fWCV+tA7wwgoew==", "dev": true, "requires": { "ansi-escapes": "2.0.0", - "chalk": "1.1.3", + "chalk": "2.0.1", "cli-cursor": "2.1.0", "cli-width": "2.1.0", "external-editor": "2.0.4", @@ -3535,9 +2488,55 @@ "run-async": "2.3.0", "rx-lite": "4.0.8", "rx-lite-aggregates": "4.0.8", - "string-width": "2.0.0", - "strip-ansi": "3.0.1", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", "through": "2.3.8" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", + "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", + "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "dev": true, + "requires": { + "ansi-styles": "3.1.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.2.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", + "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } } }, "insert-module-globals": { @@ -3576,7 +2575,8 @@ "dev": true }, "intl-pluralrules": { - "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b" + "version": "github:projectfluent/IntlPluralRules#94cb0fa1c23ad943bc5aafef43cea132fa51d68b", + "dev": true }, "ipaddr.js": { "version": "1.3.0", @@ -3584,9 +2584,9 @@ "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=" }, "irregular-plurals": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.2.0.tgz", - "integrity": "sha1-OPKZg0uowAwwvpxVThNyaXUv86w=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.3.0.tgz", + "integrity": "sha512-njf5A+Mxb3kojuHd1DzISjjIl+XhyzovXEOyPPSzdQozq/Lf2tN27mOrAAsxEPZxpn6I4MGzs1oo9TxXxPFpaA==", "dev": true }, "is-arrayish": { @@ -3595,15 +2595,6 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.8.0" - } - }, "is-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", @@ -3687,18 +2678,6 @@ "is-extglob": "1.0.0" } }, - "is-my-json-valid": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", - "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", - "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, "is-number": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", @@ -3756,12 +2735,6 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -3804,20 +2777,16 @@ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, - "is-windows": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.1.tgz", - "integrity": "sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk=" - }, "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "isobject": { "version": "2.1.0", @@ -3826,14 +2795,6 @@ "dev": true, "requires": { "isarray": "1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } } }, "jmespath": { @@ -3844,12 +2805,14 @@ "jquery": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", - "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" + "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=", + "dev": true }, "jquery-circle-progress": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/jquery-circle-progress/-/jquery-circle-progress-1.2.2.tgz", "integrity": "sha1-Jg6RMKyOK1Vy6qepO56Kaye8juo=", + "dev": true, "requires": { "jquery": "3.2.1" } @@ -3861,25 +2824,31 @@ "dev": true }, "js-tokens": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", - "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", "dev": true }, "js-yaml": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz", - "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.0.tgz", + "integrity": "sha512-0LoUNELX4S+iofCT8f4uEHIiRBR+c2AINyC8qRWfC6QNruLtxVZRJaPcu/xwMgFIgDxF25tGHaDjvxzJCNE9yw==", "dev": true, "requires": { "argparse": "1.0.9", - "esprima": "3.1.3" + "esprima": "4.0.0" } }, "jschardet": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.4.2.tgz", - "integrity": "sha1-KqEH8UKvQSHRRWWdRPUIMJYeaZo=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.5.0.tgz", + "integrity": "sha512-+Q8JsoEQbrdE+a/gg1F9XO92gcKXgpE5UACqr0sIubjDmBEkd+OOWPGzQeMrWSLxd73r4dHxBeRW7edHu5LmJQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", "dev": true }, "json-stable-stringify": { @@ -3914,6 +2883,12 @@ "through2": "0.6.5" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "jsonparse": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", @@ -3982,12 +2957,6 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", @@ -3998,6 +2967,41 @@ "through": "2.3.8" } }, + "jszip": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.3.tgz", + "integrity": "sha1-ipIEA7KxZRwPwSa+kBktkICVfDc=", + "dev": true, + "requires": { + "core-js": "2.3.0", + "es6-promise": "3.0.2", + "lie": "3.1.1", + "pako": "1.0.5", + "readable-stream": "2.0.6" + }, + "dependencies": { + "pako": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.5.tgz", + "integrity": "sha1-0iBd/ludqK95fnwWPbTR+E5GALw=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + } + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4016,6 +3020,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/l20n/-/l20n-5.0.0.tgz", "integrity": "sha1-XfkVSyCgrPAdTIsMRa8wuXpzGjM=", + "dev": true, "requires": { "fluent": "0.3.1", "fluent-intl-polyfill": "0.1.0", @@ -4031,6 +3036,14 @@ "inherits": "2.0.3", "isarray": "0.0.1", "stream-splicer": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } } }, "lazy-cache": { @@ -4048,6 +3061,12 @@ "through2": "0.6.5" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -4091,6 +3110,15 @@ "astw": "2.2.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "3.0.6" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -4221,6 +3249,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", + "dev": true, "requires": { "pseudomap": "1.0.2", "yallist": "2.1.2" @@ -4244,9 +3273,9 @@ "dev": true }, "mathml-tag-names": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.0.0.tgz", - "integrity": "sha1-7uYVESorEn5w9VjWnJ6+FAdlA9c=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.0.1.tgz", + "integrity": "sha1-jUEmgWi/htEQK5gQnijlMeejRXg=", "dev": true }, "media-typer": { @@ -4265,7 +3294,7 @@ "loud-rejection": "1.6.0", "map-obj": "1.0.1", "minimist": "1.2.0", - "normalize-package-data": "2.3.8", + "normalize-package-data": "2.4.0", "object-assign": "4.1.1", "read-pkg-up": "1.0.1", "redent": "1.0.0", @@ -4434,6 +3463,12 @@ "path-is-absolute": "1.0.1" } }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, "ms": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", @@ -4466,7 +3501,7 @@ "inherits": "2.0.3", "JSONStream": "1.3.1", "parents": "1.0.1", - "readable-stream": "2.3.2", + "readable-stream": "2.3.3", "resolve": "1.3.3", "stream-combiner2": "1.1.1", "subarg": "1.0.0", @@ -4474,16 +3509,10 @@ "xtend": "4.0.1" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -4544,13 +3573,6 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, - "nan": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", - "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", - "dev": true, - "optional": true - }, "native-promise-only": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", @@ -4574,12 +3596,12 @@ "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA=" }, "normalize-package-data": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", - "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.4.2", + "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", "semver": "5.3.0", "validate-npm-package-license": "3.0.1" @@ -4741,16 +3763,8 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "outpipe": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/outpipe/-/outpipe-1.1.1.tgz", - "integrity": "sha1-UM+GFjZeh+Ax4ppeyTOaPaRyX6I=", - "dev": true, - "requires": { - "shell-quote": "1.6.1" - } + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "pako": { "version": "0.2.9", @@ -4912,9 +3926,9 @@ } }, "platform": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.3.tgz", - "integrity": "sha1-ZGx3ARiZhwtqCQPnXpl+jlHadGE=" + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.4.tgz", + "integrity": "sha1-bw+xftqqSPIUQrOpdcBjEw8cPr0=" }, "plur": { "version": "2.1.2", @@ -4922,7 +3936,7 @@ "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", "dev": true, "requires": { - "irregular-plurals": "1.2.0" + "irregular-plurals": "1.3.0" } }, "pluralize": { @@ -4943,6 +3957,12 @@ "supports-color": "3.2.3" }, "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", @@ -5032,9 +4052,9 @@ "dev": true }, "prettier": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.4.4.tgz", - "integrity": "sha512-GuuPazIvjW1DG26yLQgO+nagmRF/h9M4RaCtZWqu/eFW7csdZkQEwPJUeXX10d+LzmCnR9DuIZndqIOn3p2YoA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.5.3.tgz", + "integrity": "sha1-WdrcaDNF7GuI+IuU7Urn4do5S/4=", "dev": true }, "process": { @@ -5046,7 +4066,8 @@ "process-nextick-args": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true }, "progress": { "version": "2.0.0", @@ -5059,7 +4080,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", "requires": { - "asap": "2.0.5" + "asap": "2.0.6" } }, "proxy-addr": { @@ -5102,7 +4123,8 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true }, "public-encrypt": { "version": "4.0.0", @@ -5225,9 +4247,10 @@ } }, "raven-js": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.16.0.tgz", - "integrity": "sha1-p5naT90ExjlD9n3rk9qg7P4QHqs=" + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.17.0.tgz", + "integrity": "sha1-d5RXrHkQUSw8LMm7bQqe61mpaew=", + "dev": true }, "raw-body": { "version": "2.2.0", @@ -5261,19 +4284,13 @@ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", "dev": true, "requires": { - "readable-stream": "2.3.2" + "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -5303,7 +4320,7 @@ "dev": true, "requires": { "load-json-file": "2.0.0", - "normalize-package-data": "2.3.8", + "normalize-package-data": "2.4.0", "path-type": "2.0.0" } }, @@ -5348,7 +4365,7 @@ "dev": true, "requires": { "load-json-file": "1.1.0", - "normalize-package-data": "2.3.8", + "normalize-package-data": "2.4.0", "path-type": "1.1.0" } }, @@ -5372,49 +4389,12 @@ "inherits": "2.0.3", "isarray": "0.0.1", "string_decoder": "0.10.31" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.2", - "set-immediate-shim": "1.0.1" }, "dependencies": { "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" } } }, @@ -5557,6 +4537,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true, "requires": { "glob": "7.1.2" }, @@ -5565,6 +4546,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "dev": true, "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -5613,7 +4595,8 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -5636,11 +4619,12 @@ "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, "selenium-webdriver": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.4.0.tgz", - "integrity": "sha1-FR90RSlNpqZsScwwB0eioX5TxSo=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.5.0.tgz", + "integrity": "sha512-1bCZYRfDy7vsu1dkLrclTLvWPxSo6rOIkxZXvB2wnzeWkEoiTKpw612EUGA3jRZxPzAzI9OlxuULJV8ge1vVXQ==", + "dev": true, "requires": { - "adm-zip": "0.4.7", + "jszip": "3.1.3", "rimraf": "2.6.1", "tmp": "0.0.30", "xml2js": "0.4.17" @@ -5650,9 +4634,7 @@ "version": "0.0.30", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "requires": { - "os-tmpdir": "1.0.2" - } + "dev": true } } }, @@ -5693,12 +4675,6 @@ "send": "0.15.3" } }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, "setprototypeof": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", @@ -5727,6 +4703,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, "requires": { "shebang-regex": "1.0.0" } @@ -5734,7 +4711,8 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true }, "shell-quote": { "version": "1.6.1", @@ -5782,9 +4760,9 @@ "dev": true }, "sinon": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.5.tgz", - "integrity": "sha1-mi/A/41SbacW8wlTqixl1RiRf2w=", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.3.8.tgz", + "integrity": "sha1-Md4G/tj7o6Zx5XbdltClhjeW8lw=", "dev": true, "requires": { "diff": "3.2.0", @@ -5797,6 +4775,12 @@ "type-detect": "4.0.3" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "path-to-regexp": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", @@ -5844,9 +4828,9 @@ "dev": true }, "specificity": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.0.tgz", - "integrity": "sha1-MyRy1OXrWvIIIRcZM5mKa8Oxzm8=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.1.tgz", + "integrity": "sha1-8bBoQkzjF64HR42V3jwhz4Xo1Wc=", "dev": true }, "split": { @@ -5867,6 +4851,12 @@ "through2": "0.6.5" }, "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -5914,19 +4904,13 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.2" + "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -5965,19 +4949,13 @@ "dev": true, "requires": { "duplexer2": "0.1.4", - "readable-stream": "2.3.2" + "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -6008,21 +4986,15 @@ "requires": { "builtin-status-codes": "3.0.0", "inherits": "2.0.3", - "readable-stream": "2.3.2", + "readable-stream": "2.3.3", "to-arraybuffer": "1.0.1", "xtend": "4.0.1" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -6052,19 +5024,13 @@ "dev": true, "requires": { "inherits": "2.0.3", - "readable-stream": "2.3.2" + "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -6103,13 +5069,30 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" }, "string-width": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz", - "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "3.0.1" + "strip-ansi": "4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } } }, "string.prototype.padend": { @@ -6206,16 +5189,16 @@ } }, "stylelint": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.11.1.tgz", - "integrity": "sha1-yBbGWLr32eXRZ9gic/6tN8l65J0=", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-7.13.0.tgz", + "integrity": "sha1-ER+Xttpy53XICADWu29fhpmXeF0=", "dev": true, "requires": { "autoprefixer": "6.7.7", "balanced-match": "0.4.2", - "chalk": "1.1.3", + "chalk": "2.0.1", "colorguard": "1.2.0", - "cosmiconfig": "2.1.3", + "cosmiconfig": "2.2.1", "debug": "2.6.7", "doiuse": "2.6.0", "execall": "1.0.0", @@ -6223,13 +5206,13 @@ "get-stdin": "5.0.1", "globby": "6.1.0", "globjoin": "0.1.4", - "html-tags": "1.2.0", + "html-tags": "2.0.0", "ignore": "3.3.3", "imurmurhash": "0.1.4", "known-css-properties": "0.2.0", "lodash": "4.17.4", "log-symbols": "1.0.2", - "mathml-tag-names": "2.0.0", + "mathml-tag-names": "2.0.1", "meow": "3.7.0", "micromatch": "2.3.11", "normalize-selector": "0.2.0", @@ -6243,8 +5226,8 @@ "postcss-selector-parser": "2.2.3", "postcss-value-parser": "3.3.0", "resolve-from": "3.0.0", - "specificity": "0.3.0", - "string-width": "2.0.0", + "specificity": "0.3.1", + "string-width": "2.1.1", "style-search": "0.1.0", "stylehacks": "2.3.2", "sugarss": "0.2.0", @@ -6252,16 +5235,36 @@ "table": "4.0.1" }, "dependencies": { + "ansi-styles": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", + "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, "balanced-match": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", "dev": true }, + "chalk": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", + "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "dev": true, + "requires": { + "ansi-styles": "3.1.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.2.0" + } + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "1.0.0", @@ -6290,6 +5293,15 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true + }, + "supports-color": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", + "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } }, @@ -6329,6 +5341,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.5.2.tgz", "integrity": "sha1-M2GjlxVnUEw1EGOr6q4PqiPb8/g=", + "dev": true, "requires": { "component-emitter": "1.2.1", "cookiejar": "2.1.1", @@ -6342,15 +5355,11 @@ "readable-stream": "2.3.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", + "dev": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -6365,6 +5374,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", + "dev": true, "requires": { "safe-buffer": "5.1.1" } @@ -6375,6 +5385,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", + "dev": true, "requires": { "methods": "1.1.2", "superagent": "3.5.2" @@ -6425,9 +5436,36 @@ "chalk": "1.1.3", "lodash": "4.17.4", "slice-ansi": "0.0.4", - "string-width": "2.0.0" + "string-width": "2.1.1" + }, + "dependencies": { + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + } } }, + "testpilot-ga": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/testpilot-ga/-/testpilot-ga-0.3.0.tgz", + "integrity": "sha512-z4PJbw3KK0R0iflA+u/3BhWZrtsLHLu+0rMviMd8H1wp8qPV0rggNBjsKckBJCcXq4uEjXETGZzApHH7Tovpzw==", + "dev": true + }, "text-encoding": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", @@ -6452,20 +5490,14 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "2.3.2", + "readable-stream": "2.3.3", "xtend": "4.0.1" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, "readable-stream": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", - "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { "core-util-is": "1.0.2", @@ -6565,19 +5597,22 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, - "uglify-es": { - "version": "3.0.19", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.19.tgz", - "integrity": "sha1-U9RI3fWXcMHq++LiV6J+L0hR0OU=", + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "optional": true, "requires": { - "commander": "2.9.0", - "source-map": "0.5.6" + "source-map": "0.5.6", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" }, "dependencies": { "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "optional": true } } }, @@ -6587,6 +5622,43 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uglifyify": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/uglifyify/-/uglifyify-4.0.3.tgz", + "integrity": "sha512-k+XKvVDmLOTVOS34sEBQdeUL0Od9zN0a1lzjJZMgkAaoNarskzFFWcpjpyh6lM7o9yRGNfbCIGdWOjIKZq6EAg==", + "dev": true, + "requires": { + "convert-source-map": "1.1.3", + "extend": "1.3.0", + "minimatch": "3.0.4", + "through": "2.3.8", + "uglify-es": "3.0.25" + }, + "dependencies": { + "extend": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz", + "integrity": "sha1-0VFvsP9WJNLr+RI+odrFoZlABPg=", + "dev": true + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "uglify-es": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.0.25.tgz", + "integrity": "sha512-UcW+mvxBCW0/3YxJRFl5w7Fsoz/GMx63aujVcLbeyfpLh8RcaEv/KjVUpl4/2YM7Zhscr5B68UCbOB67umG/EQ==", + "dev": true, + "requires": { + "commander": "2.9.0", + "source-map": "0.5.6" + } + } + } + }, "umd": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", @@ -6638,7 +5710,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "utils-merge": { "version": "1.0.0", @@ -6674,25 +5747,11 @@ "indexof": "0.0.1" } }, - "watchify": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", - "integrity": "sha1-8HX9LoqGrN6Eztum5cKgvt1SPZ4=", - "dev": true, - "requires": { - "anymatch": "1.3.0", - "browserify": "14.4.0", - "chokidar": "1.7.0", - "defined": "1.0.0", - "outpipe": "1.1.1", - "through2": "2.0.3", - "xtend": "4.0.1" - } - }, "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true, "requires": { "isexe": "2.0.0" } @@ -6758,7 +5817,8 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true }, "yargs": { "version": "3.10.0", diff --git a/package.json b/package.json index 3e3cfd54..ccc149da 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,43 @@ { "name": "firefox-send", "description": "File Sharing Experiment", - "version": "0.1.2", + "version": "0.2.1", "author": "Mozilla (https://mozilla.org)", "dependencies": { - "aws-sdk": "^2.62.0", + "aws-sdk": "^2.89.0", "body-parser": "^1.17.2", "bytes": "^2.5.0", "connect-busboy": "0.0.2", "convict": "^3.0.0", - "cross-env": "^5.0.1", "express": "^4.15.3", "express-handlebars": "^3.0.0", - "helmet": "^3.6.1", - "jquery": "^3.2.1", - "jquery-circle-progress": "^1.2.2", - "l20n": "^5.0.0", + "helmet": "^3.8.0", "mozlog": "^2.1.1", "raven": "^2.1.0", - "raven-js": "^3.16.0", - "redis": "^2.7.1", - "selenium-webdriver": "^3.4.0", - "supertest": "^3.0.0", - "uglify-es": "3.0.19" + "redis": "^2.7.1" }, "devDependencies": { "browserify": "^14.4.0", - "eslint": "^4.0.0", + "eslint": "^4.3.0", "eslint-plugin-mocha": "^4.11.0", - "eslint-plugin-node": "^5.0.0", + "eslint-plugin-node": "^5.1.1", "eslint-plugin-security": "^1.4.0", "git-rev-sync": "^1.9.1", + "jquery": "^3.2.1", + "jquery-circle-progress": "^1.2.2", + "l20n": "^5.0.0", "mocha": "^3.4.2", "npm-run-all": "^4.0.2", - "prettier": "^1.4.4", + "prettier": "^1.5.3", "proxyquire": "^1.8.0", - "sinon": "^2.3.5", - "stylelint": "^7.11.0", + "raven-js": "^3.17.0", + "selenium-webdriver": "^3.5.0", + "sinon": "^2.3.8", + "stylelint": "^7.13.0", "stylelint-config-standard": "^16.0.0", - "watchify": "^3.9.0" + "supertest": "^3.0.0", + "testpilot-ga": "^0.3.0", + "uglifyify": "^4.0.3" }, "engines": { "node": ">=8.0.0" @@ -47,15 +46,20 @@ "license": "MPL-2.0", "repository": "mozilla/send", "scripts": { - "predocker": "browserify frontend/src/main.js | uglifyjs > public/bundle.js && npm run version", - "dev": "npm run version && watchify frontend/src/main.js -o public/bundle.js -d | node server/server", - "format": "prettier '{frontend/src/,scripts/,server/,test/}*.js' 'public/*.css' --single-quote --write", + "build": "npm-run-all build:*", + "build:upload": "browserify frontend/src/upload.js -g uglifyify -o public/upload.js", + "build:download": "browserify frontend/src/download.js -g uglifyify -o public/download.js", + "build:version": "node scripts/version", + "build:l10n": "cp node_modules/l20n/dist/web/l20n.min.js public", + "dev": "npm run build && npm start", + "format": "prettier '{frontend/src/,scripts/,server/,test/**/}*.js' 'public/*.css' --single-quote --write", "lint": "npm-run-all lint:*", "lint:css": "stylelint 'public/*.css'", "lint:js": "eslint .", "start": "node server/server", - "test": "mocha test/unit && mocha test/server && npm run test-browser && node test/frontend/driver.js", - "test-browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d", - "version": "node scripts/version" + "test": "npm-run-all test:*", + "test:unit": "mocha test/unit", + "test:server": "mocha test/server", + "test:browser": "browserify test/frontend/frontend.bundle.js -o test/frontend/bundle.js -d && node test/frontend/driver.js" } } diff --git a/public/download.js b/public/download.js index 45997e2d..932d9e26 100644 --- a/public/download.js +++ b/public/download.js @@ -2,10 +2,10 @@ window.Raven=require("raven-js"),window.Raven.config(window.dsn).install(),window.dsn=void 0;const testPilotGA=require("testpilot-ga");window.analytics=new testPilotGA({an:"Firefox Send",ds:"web",tid:window.trackerId}); },{"raven-js":13,"testpilot-ga":17}],2:[function(require,module,exports){ -require("./common");const FileReceiver=require("./fileReceiver"),{notify:notify,findMetric:findMetric,sendEvent:sendEvent}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;$(document).ready(function(){$(".send-new").attr("href",window.location.origin),$(".send-new").click(function(e){e.preventDefault(),sendEvent("recipient","restarted",{cd2:"completed"}).then(()=>{location.href=e.currentTarget.href})}),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("recipient","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#expired-send-new").click(function(){storage.referrer="errored-download"});const e=$("#dl-filename").html(),t=Number($("#dl-bytelength").text()),o=Number($("#dl-ttl").text());$("#dl-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#00C8D7",size:158,animation:{duration:300}}),$("#download-btn").click(function(){storage.totalDownloads+=1;const n=new FileReceiver,r=storage.numFiles;n.on("progress",o=>{window.onunload=function(){storage.referrer="cancelled-download",sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"cancelled"})},$("#download-page-one").attr("hidden",!0),$("#download-progress").removeAttr("hidden");const a=o[0]/o[1];$("#dl-progress").circleProgress("value",a),$(".percent-number").html(`${Math.floor(100*a)}`),$(".progress-text").text(`${e} (${bytes(o[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(o[1],{decimalPlaces:1})})`),1===a&&(n.removeAllListeners("progress"),document.l10n.formatValues("downloadNotification","downloadFinish").then(e=>{notify(e[0]),$(".title").html(e[1])}),window.onunload=null)});let a;n.on("decrypting",e=>{e?console.log("Decrypting"):(console.log("Done decrypting"),a=Date.now())}),n.on("hashing",e=>{e?console.log("Checking file integrity"):console.log("Integrity check done")});const l=Date.now();sendEvent("recipient","download-started",{cm1:t,cm4:o,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads}),n.download().catch(e=>{sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"errored",cd6:e}),document.l10n.formatValue("expiredPageHeader").then(e=>{$(".title").text(e)}),$("#download-btn").attr("hidden",!0),$("#expired-img").removeAttr("hidden"),console.log("The file has expired, or has already been deleted.")}).then(([e,o])=>{const n=Date.now(),d=n-l,c=t/((n-a)/1e3);storage.referrer="completed-download",sendEvent("recipient","download-stopped",{cm1:t,cm2:d,cm3:c,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"completed"});const i=new DataView(e),s=new Blob([i]),g=URL.createObjectURL(s),m=document.createElement("a");m.href=g,window.navigator.msSaveBlob?window.navigator.msSaveBlob(s,o):(m.download=o,document.body.appendChild(m),m.click())}).catch(e=>(Raven.captureException(e),Promise.reject(e)))})}); +require("./common");const FileReceiver=require("./fileReceiver"),{notify:notify,findMetric:findMetric,gcmCompliant:gcmCompliant,sendEvent:sendEvent}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;$(document).ready(function(){gcmCompliant().catch(e=>{$("#download").attr("hidden",!0),sendEvent("recipient","unsupported",{cd6:e}).then(()=>{location.replace("/unsupported")})}),$(".send-new").attr("href",window.location.origin),$(".send-new").click(function(e){e.preventDefault(),sendEvent("recipient","restarted",{cd2:"completed"}).then(()=>{location.href=e.currentTarget.href})}),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("recipient","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})});const e=$("#dl-filename").text(),t=Number($("#dl-bytelength").text()),o=Number($("#dl-ttl").text());$("#dl-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$("#download-btn").click(function(){storage.totalDownloads+=1;const n=new FileReceiver,r=storage.numFiles;n.on("progress",o=>{window.onunload=function(){storage.referrer="cancelled-download",sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"cancelled"})},$("#download-page-one").attr("hidden",!0),$("#download-progress").removeAttr("hidden");const n=o[0]/o[1];$("#dl-progress").circleProgress("value",n),$(".percent-number").text(`${Math.floor(100*n)}`),$(".progress-text").text(`${e} (${bytes(o[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(o[1],{decimalPlaces:1})})`)});let a;n.on("decrypting",e=>{e?(n.removeAllListeners("progress"),window.onunload=null,document.l10n.formatValue("decryptingFile").then(e=>{$(".progress-text").text(e)})):(console.log("Done decrypting"),a=Date.now())}),n.on("hashing",e=>{e?document.l10n.formatValue("verifyingFile").then(e=>{$(".progress-text").text(e)}):($(".progress-text").text(" "),document.l10n.formatValues("downloadNotification","downloadFinish").then(e=>{notify(e[0]),$(".title").text(e[1])}))});const d=Date.now();sendEvent("recipient","download-started",{cm1:t,cm4:o,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads}),n.download().catch(e=>{sendEvent("recipient","download-stopped",{cm1:t,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"errored",cd6:e}),document.l10n.formatValue("expiredPageHeader").then(e=>{$(".title").text(e)}),$("#download-btn").attr("hidden",!0),$("#expired-img").removeAttr("hidden"),console.log("The file has expired, or has already been deleted.")}).then(([e,o])=>{const n=Date.now(),l=n-d,c=t/((n-a)/1e3);storage.referrer="completed-download",sendEvent("recipient","download-stopped",{cm1:t,cm2:l,cm3:c,cm5:storage.totalUploads,cm6:r,cm7:storage.totalDownloads,cd2:"completed"});const i=new DataView(e),s=new Blob([i]),m=URL.createObjectURL(s),p=document.createElement("a");p.href=m,window.navigator.msSaveBlob?window.navigator.msSaveBlob(s,o):(p.download=o,document.body.appendChild(p),p.click())}).catch(e=>(Raven.captureException(e),Promise.reject(e)))})}); },{"./common":1,"./fileReceiver":3,"./storage":4,"./utils":5,"bytes":6,"jquery":9,"jquery-circle-progress":8}],3:[function(require,module,exports){ -const EventEmitter=require("events"),{hexToArray:hexToArray}=require("./utils");class FileReceiver extends EventEmitter{constructor(){super()}download(){return Promise.all([new Promise((e,t)=>{const r=new XMLHttpRequest;r.onprogress=(e=>{e.lengthComputable&&404!==e.target.status&&this.emit("progress",[e.loaded,e.total])}),r.onload=function(i){if(404===r.status)return void t(new Error("The file has expired, or has already been deleted."));const a=new Blob([this.response]),s=new FileReader;s.onload=function(){const t=JSON.parse(r.getResponseHeader("X-File-Metadata"));e({data:this.result,aad:t.aad,filename:t.filename,iv:t.id})},s.readAsArrayBuffer(a)},r.open("get","/assets"+location.pathname.slice(0,-1),!0),r.responseType="blob",r.send()}),window.crypto.subtle.importKey("jwk",{kty:"oct",k:location.hash.slice(1),alg:"A128GCM",ext:!0},{name:"AES-GCM"},!0,["encrypt","decrypt"])]).then(([e,t])=>(this.emit("decrypting",!0),Promise.all([window.crypto.subtle.decrypt({name:"AES-GCM",iv:hexToArray(e.iv),additionalData:hexToArray(e.aad)},t,e.data).then(e=>(this.emit("decrypting",!1),Promise.resolve(e))),e.filename,hexToArray(e.aad)]))).then(([e,t,r])=>(this.emit("hashing",!0),window.crypto.subtle.digest("SHA-256",e).then(i=>(this.emit("hashing",!1),new Uint8Array(i).toString()===r.toString()?(this.emit("safe",!0),Promise.all([e,decodeURIComponent(t)])):(this.emit("unsafe",!0),Promise.reject())))))}}module.exports=FileReceiver; +const EventEmitter=require("events"),{hexToArray:hexToArray}=require("./utils");class FileReceiver extends EventEmitter{constructor(){super()}download(){return Promise.all([new Promise((e,t)=>{const r=new XMLHttpRequest;r.onprogress=(e=>{e.lengthComputable&&404!==e.target.status&&this.emit("progress",[e.loaded,e.total])}),r.onload=function(a){if(404===r.status)return void t(new Error("The file has expired, or has already been deleted."));const i=new Blob([this.response]),s=new FileReader;s.onload=function(){const t=JSON.parse(r.getResponseHeader("X-File-Metadata"));e({data:this.result,aad:t.aad,filename:t.filename,iv:t.id})},s.readAsArrayBuffer(i)},r.open("get","/assets"+location.pathname.slice(0,-1),!0),r.responseType="blob",r.send()}),window.crypto.subtle.importKey("jwk",{kty:"oct",k:location.hash.slice(1),alg:"A128GCM",ext:!0},{name:"AES-GCM"},!0,["encrypt","decrypt"])]).then(([e,t])=>(this.emit("decrypting",!0),Promise.all([window.crypto.subtle.decrypt({name:"AES-GCM",iv:hexToArray(e.iv),additionalData:hexToArray(e.aad),tagLength:128},t,e.data).then(e=>(this.emit("decrypting",!1),Promise.resolve(e))),e.filename,hexToArray(e.aad)]))).then(([e,t,r])=>(this.emit("hashing",!0),window.crypto.subtle.digest("SHA-256",e).then(a=>(this.emit("hashing",!1),new Uint8Array(a).toString()===r.toString()?(this.emit("safe",!0),Promise.all([e,decodeURIComponent(t)])):(this.emit("unsafe",!0),Promise.reject())))))}}module.exports=FileReceiver; },{"./utils":5,"events":7}],4:[function(require,module,exports){ const{isFile:isFile}=require("./utils");class Storage{constructor(e){this.engine=e}get totalDownloads(){return Number(this.engine.getItem("totalDownloads"))}set totalDownloads(e){this.engine.setItem("totalDownloads",e)}get totalUploads(){return Number(this.engine.getItem("totalUploads"))}set totalUploads(e){this.engine.setItem("totalUploads",e)}get referrer(){return this.engine.getItem("referrer")}set referrer(e){this.engine.setItem("referrer",e)}get files(){const e=[];for(let t=0;tTerms of Service and Privacy Notice. You can learn more about this experiment and its data collection here. +legalNoticeMozilla = Use of the Firefox Send website is also subject to Mozilla’s Websites Privacy Notice and Websites Terms of Use. +deletePopupText = Delete this file? +deletePopupYes = Yes +deletePopupCancel = Cancel deleteButtonHover .title = Delete copyUrlHover diff --git a/public/main.css b/public/main.css index e1a1bf7b..5677378b 100644 --- a/public/main.css +++ b/public/main.css @@ -1,7 +1,12 @@ /*** index.html ***/ html { background: url('resources/send_bg.svg'); - font-family: 'SF Pro Text', sans-serif; + font-family: -apple-system, + BlinkMacSystemFont, + 'SF Pro Text', + Helvetica, + Arial, + sans-serif; font-weight: 200; background-size: 100%; background-repeat: no-repeat; @@ -10,24 +15,95 @@ html { } body { - min-height: 100%; - position: relative; + display: flex; + flex-direction: column; margin: 0; + min-height: 100vh; + position: relative; +} + +.header { + align-items: flex-start; + box-sizing: border-box; + display: flex; + justify-content: space-between; + padding: 31px; + width: 100%; } .send-logo { + display: flex; position: relative; - top: 31px; - left: 31px; - display: inline-block; + align-items: center; +} + +.site-title { + color: #3e3d40; + font-size: 32px; + font-weight: 500; + margin: 0; + position: relative; + top: -1px; +} + +.site-subtitle { + color: #3e3d40; + font-size: 12px; + margin: 0 8px; +} + +.site-subtitle a { + font-weight: bold; + color: #3e3d40; + transition: color 50ms; +} + +.send-logo:hover a { + color: #0297f8; +} + +.feedback { + background-color: #0297f8; + background-image: url('resources/feedback.svg'); + background-position: 4px 6px; + background-repeat: no-repeat; + background-size: 14px; + border-radius: 3px; + border: 1px solid #0297f8; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + color: #fff; + cursor: pointer; + display: block; + float: right; + font-size: 12px; + line-height: 12px; + opacity: 0.9; + padding: 6px 6px 5px 20px; +} + +.feedback:hover, +.feedback:focus { + background-color: #0287e8; +} + +.feedback:active { + background-color: #0277d8; } .all { - padding-top: 10%; - padding-bottom: 51px; + flex: 1; + display: flex; + flex-direction: column; + justify-content: flex-start; + max-width: 630px; + margin: 0 auto; + width: 96%; } -input, select, textarea, button { +input, +select, +textarea, +button { font-family: inherit; } @@ -38,24 +114,26 @@ a { /** page-one **/ .title { font-size: 33px; + line-height: 40px; margin: 20px auto; text-align: center; + max-width: 520px; font-family: 'SF Pro Display', sans-serif; } .description { font-size: 15px; line-height: 23px; - width: 630px; + max-width: 630px; text-align: center; margin: 0 auto 60px; - color: #0C0C0D; + color: #0c0c0d; + width: 92%; } .upload-window { border: 1px dashed rgba(0, 148, 251, 0.5); margin: 0 auto; - width: 640px; height: 255px; border-radius: 4px; display: flex; @@ -63,14 +141,15 @@ a { align-items: center; flex-direction: column; text-align: center; + transition: transform 150ms; + padding: 15px; } .upload-window.ondrag { border: 3px dashed rgba(0, 148, 251, 0.5); margin: 0 auto; - width: 636px; height: 251px; - transform: scale(1.05); + transform: scale(1.04); border-radius: 4.2px; display: flex; justify-content: center; @@ -80,7 +159,7 @@ a { } .link { - color: #0094FB; + color: #0094fb; text-decoration: none; } @@ -92,10 +171,10 @@ a { } #browse { - background: #0297F8; + background: #0297f8; border-radius: 5px; font-size: 15px; - color: #FFF; + color: #fff; width: 240px; height: 44px; display: flex; @@ -104,12 +183,17 @@ a { cursor: pointer; } +#browse:hover { + background-color: #0287e8; +} + input[type="file"] { display: none; } #file-size-msg { font-size: 12px; + line-height: 16px; color: #737373; margin-bottom: 22px; } @@ -129,9 +213,10 @@ th { td { font-size: 15px; vertical-align: top; - color: #4A4A4A; + color: #4a4a4a; padding: 17px 19px 0; line-height: 23px; + position: relative; } table { @@ -141,42 +226,44 @@ table { tbody { word-wrap: break-word; + word-break: break-all; } #uploaded-files { - width: 640px; margin: 45.3px auto; table-layout: fixed; } -.icon-delete, .icon-copy, .icon-check { +.icon-delete, +.icon-copy, +.icon-check { cursor: pointer; } /* Popup container */ .popup { - position: relative; + position: absolute; display: inline-block; - cursor: pointer; } /* The actual popup (appears on top) */ .popup .popuptext { visibility: hidden; - width: 160px; - background-color: #555; - color: #FFF; + min-width: 115px; + background-color: #fff; + color: #000; + border: 1px solid #0297f8; text-align: center; - border-radius: 6px; - padding: 8px 0; + border-radius: 5px; + padding: 7px 8px; position: absolute; z-index: 1; - bottom: 20px; - left: 50%; - margin-left: -88px; + bottom: 8px; + right: -28px; transition: opacity 0.5s; opacity: 0; outline: 0; + box-shadow: 3px 3px 7px #888; } /* Popup arrow */ @@ -184,11 +271,11 @@ tbody { content: ""; position: absolute; top: 100%; - left: 50%; + right: 30px; margin-left: -5px; border-width: 5px; border-style: solid; - border-color: #555 transparent transparent; + border-color: #0297f8 transparent transparent; } .popup .show { @@ -196,6 +283,29 @@ tbody { opacity: 1; } +.popup-message { + margin-bottom: 4px; +} + +.popup-yes { + color: #fff; + background-color: #0297f8; + border-radius: 5px; + padding: 2px 11px; + cursor: pointer; +} + +.popup-yes:hover { + background-color: #0287e8; +} + +.popup-no { + color: #4a4a4a; + border-radius: 6px; + padding: 3px 5px; + cursor: pointer; +} + /** upload-progress **/ .progress-bar { margin-top: 3px; @@ -239,14 +349,13 @@ tbody { } #cancel-upload { - color: #D70022; + color: #d70022; cursor: pointer; text-decoration: underline; } /** share-link **/ #share-window { - width: 645px; margin: 0 auto; display: flex; justify-content: center; @@ -262,53 +371,59 @@ tbody { #copy { display: flex; flex-wrap: nowrap; + width: 640px; } #copy-text { align-self: flex-start; margin-top: 60px; margin-bottom: 10px; - color: #0C0C0D; + color: #0c0c0d; } #link { - width: 480px; + flex: 1; height: 56px; - border: 1px solid #0297F8; + border: 1px solid #0297f8; border-radius: 6px 0 0 6px; font-size: 24px; color: #737373; font-family: 'SF Pro Display', sans-serif; letter-spacing: 0; line-height: 23px; + padding-left: 5px; + padding-right: 5px; } #link:disabled { - border: 1px solid #05A700; - background: #FFF; + border: 1px solid #05a700; + background: #fff; } #copy-btn { - width: 165px; - height: 60px; - background: #0297F8; - border: 1px solid #0297F8; + flex: 0 1 165px; + background: #0297f8; border-radius: 0 6px 6px 0; + border: 1px solid #0297f8; color: white; cursor: pointer; font-size: 15px; + height: 60px; + padding-left: 10px; + padding-right: 10px; + white-space: nowrap; } #copy-btn:disabled { - background: #05A700; - border: 1px solid #05A700; + background: #05a700; + border: 1px solid #05a700; cursor: auto; } #delete-file { width: 176px; height: 44px; - background: #FFF; + background: #fff; border: 1px solid rgba(12, 12, 13, 0.3); border-radius: 5px; font-size: 15px; @@ -322,7 +437,7 @@ tbody { font-size: 15px; margin: auto; text-align: center; - color: #0094FB; + color: #0094fb; cursor: pointer; text-decoration: underline; } @@ -336,7 +451,8 @@ tbody { text-align: center; } -#upload-error[hidden], #unsupported-browser[hidden] { +#upload-error[hidden], +#unsupported-browser[hidden] { display: none; } @@ -356,9 +472,8 @@ tbody { .unsupported-description { font-size: 13px; line-height: 23px; - width: 630px; text-align: center; - color: #7D7D7D; + color: #7d7d7d; margin: 0 auto 23px; } @@ -370,14 +485,14 @@ tbody { margin-bottom: 181px; width: 260px; height: 80px; - background: #12BC00; + background: #12bc00; border-radius: 3px; cursor: pointer; border: 0; box-shadow: 0 5px 3px rgb(234, 234, 234); font-family: 'Fira Sans'; font-weight: 500; - color: #FFF; + color: #fff; font-size: 26px; display: flex; justify-content: center; @@ -406,15 +521,15 @@ tbody { margin-top: 20px; margin-bottom: 30px; text-align: center; - background: #0297F8; - border: 1px solid #0297F8; + background: #0297f8; + border: 1px solid #0297f8; border-radius: 5px; font-weight: 300; cursor: pointer; } #download-btn:disabled { - background: #47B04B; + background: #47b04b; cursor: auto; } @@ -434,9 +549,8 @@ tbody { .expired-description { font-size: 15px; line-height: 23px; - width: 630px; text-align: center; - color: #7D7D7D; + color: #7d7d7d; margin: 0 auto 23px; } @@ -460,14 +574,13 @@ tbody { /* footer */ .footer { - position: absolute; right: 0; bottom: 0; left: 0; font-size: 15px; display: flex; align-items: flex-end; - padding: 10px; + padding: 50px 10px 10px; } .mozilla-logo { @@ -495,8 +608,69 @@ tbody { margin-left: 30px; } -.github, .twitter { +.github, +.twitter { width: 32px; height: 32px; margin-bottom: -5px; } + +@media (max-device-width: 768px) { + .description { + margin: 0 auto 25px; + } + + #copy { + width: 100%; + } + + #link { + font-size: 18px; + } + + .mozilla-logo { + margin-left: -7px; + } + + .legal-links > * { + display: block; + padding: 10px 0; + } +} + +@media (max-device-width: 520px) { + .header { + flex-direction: column; + justify-content: flex-start; + } + + .feedback { + margin-top: 10px; + } + + #copy { + width: 100%; + flex-direction: column; + } + + #link { + font-size: 22px; + padding: 15px 10px; + border-radius: 6px 6px 0 0; + } + + #copy-btn { + border-radius: 0 0 6px 6px; + flex: 0 1 65px; + } + + th { + font-size: 14px; + padding: 0 5px; + } + + td { + font-size: 13px; + padding: 17px 5px 0; + } +} diff --git a/public/resources/feedback.svg b/public/resources/feedback.svg new file mode 100644 index 00000000..1e3e3fe5 --- /dev/null +++ b/public/resources/feedback.svg @@ -0,0 +1 @@ +Combined Shape \ No newline at end of file diff --git a/public/resources/send_logo_type.svg b/public/resources/send_logo_type.svg deleted file mode 100644 index 07091885..00000000 --- a/public/resources/send_logo_type.svg +++ /dev/null @@ -1 +0,0 @@ -logo design_01 copy \ No newline at end of file diff --git a/public/upload.js b/public/upload.js index 1698586e..0a50fe60 100644 --- a/public/upload.js +++ b/public/upload.js @@ -8,7 +8,7 @@ const EventEmitter=require("events"),{arrayToHex:arrayToHex}=require("./utils"), const{isFile:isFile}=require("./utils");class Storage{constructor(e){this.engine=e}get totalDownloads(){return Number(this.engine.getItem("totalDownloads"))}set totalDownloads(e){this.engine.setItem("totalDownloads",e)}get totalUploads(){return Number(this.engine.getItem("totalUploads"))}set totalUploads(e){this.engine.setItem("totalUploads",e)}get referrer(){return this.engine.getItem("referrer")}set referrer(e){this.engine.setItem("referrer",e)}get files(){const e=[];for(let t=0;t1||0===o.originalEvent.dataTransfer.files[0].size)return $(".upload-window").removeClass("ondrag"),void document.l10n.formatValue("uploadPageMultipleFilesAlert").then(e=>{alert(e)});n=o.originalEvent.dataTransfer.files[0]}else n=o.target.files[0];if(n.size>MAXFILESIZE)return document.l10n.formatValue("fileTooBig",{size:bytes(MAXFILESIZE)}).then(alert);$("#page-one").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#upload-progress").removeAttr("hidden"),$("body").off("drop",e);const r=new FileSender(n);$("#cancel-upload").click(()=>{r.cancel(),location.reload(),document.l10n.formatValue("uploadCancelNotification").then(e=>{notify(e)}),storage.referrer="cancelled-upload",sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"cancelled"})}),r.on("progress",e=>{const t=e[0]/e[1];$("#ul-progress").circleProgress("value",t),$("#ul-progress").circleProgress().on("circle-animation-end",function(){$(".percent-number").html(`${Math.floor(100*t)}`)}),$(".progress-text").text(`${n.name} (${bytes(e[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(e[1],{decimalPlaces:1})})`)}),r.on("loading",e=>{e?console.log("Processing"):console.log("Finished processing")}),r.on("hashing",e=>{e?console.log("Hashing"):console.log("Finished hashing")});let a;r.on("encrypting",e=>{e?console.log("Encrypting"):(console.log("Finished encrypting"),a=Date.now())});let d;const l=Date.now(),c=storage.numFiles+1;sendEvent("sender","upload-started",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd5:window.referrer}),r.upload().then(e=>{const r=Date.now(),s=r-l,i=r-a,p=n.size/(i/1e3);sendEvent("sender","upload-stopped",{cm1:n.size,cm2:s,cm3:p,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"completed"});const m={name:n.name,size:n.size,fileId:e.fileId,url:e.url,secretKey:e.secretKey,deleteToken:e.deleteToken,creationDate:new Date,expiry:864e5,totalTime:s,typeOfUpload:"drop"===o.type?"drop":"click",uploadSpeed:p};storage.addFile(e.fileId,m),$("#upload-filename").attr("data-l10n-id","uploadSuccessConfirmHeader"),d=window.setTimeout(()=>{$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#share-link").removeAttr("hidden")},1e3),t(m),document.l10n.formatValue("notifyUploadDone").then(e=>{notify(e)})}).catch(e=>{Raven.captureException(e),console.log(e),$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").removeAttr("hidden"),window.clearTimeout(d),sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:c,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"errored",cd6:e})})}function t(e){function t(){v=h.getTime()-Date.now(),w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);let n;y>=1?(l.innerHTML=y+"h "+w%60+"m",n=window.setTimeout(()=>{t()},6e4)):0===y&&(l.innerHTML=w+"m "+E+"s",n=window.setTimeout(()=>{t()},1e3)),v<=0&&(storage.remove(e.fileId),$(l).parents("tr").remove(),window.clearTimeout(n),o())}const n=document.createElement("tr"),r=document.createElement("td"),a=document.createElement("td"),d=$("",{src:"/resources/copy-16.svg",class:"icon-copy","data-l10n-id":"copyUrlHover"}),l=document.createElement("td"),c=document.createElement("td"),s=$("",{src:"/resources/close-16.svg",class:"icon-delete","data-l10n-id":"deleteButtonHover"}),i=document.createElement("div"),p=$("
",{class:"popuptext"}),m=document.createTextNode(e.name),u=e.url.trim()+`#${e.secretKey}`.trim();$("#link").attr("value",u),$("#copy-text").attr("data-l10n-args",'{"filename": "'+e.name+'"}'),$("#copy-text").attr("data-l10n-id","copyUrlFormLabelWithName"),p.attr("tabindex","-1"),r.appendChild(m);const g=document.createElement("span");$(g).addClass("icon-cancel-1"),$(g).attr("data-l10n-id","deleteButtonHover"),c.appendChild(g);const f=document.createElement("span");$(f).addClass("icon-docs"),$(f).attr("data-l10n-id","copyUrlHover"),a.appendChild(f),a.style.color="#0A8DFF",d.click(function(){sendEvent("sender","copied",{cd4:"upload-list"});const e=document.createElement("input");e.setAttribute("value",u),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),document.l10n.formatValue("copiedUrl").then(e=>{a.innerHTML=e}),window.setTimeout(()=>{const e=document.createElement("img");$(e).addClass("icon-copy"),$(e).attr("data-l10n-id","copyUrlHover"),$(e).attr("src","/resources/copy-16.svg"),$(a).html(e)},500)}),e.creationDate=new Date(e.creationDate);const h=new Date;h.setTime(e.creationDate.getTime()+e.expiry);let v=0;v=h.getTime()-Date.now();let w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);t(),i.classList.add("popup");const D=document.createElement("span");$(D).addClass("del-file"),$(D).attr("data-l10n-id","sentFilesTitle4");const T=document.createElement("span");$(T).addClass("nvm"),$(T).attr("data-l10n-id","nevermindButton"),p.html([D," "," ",T]),n.appendChild(r),$(a).append(d),n.appendChild(a),n.appendChild(l),$(i).append(p),$(c).append(s),c.appendChild(i),n.appendChild(c),$("tbody").append(n);const k=storage.numFiles;p.find(".del-file").click(t=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{$(t.target).parents("tr").remove();const n=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:n,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"upload-list"}).then(()=>{storage.remove(e.fileId)}),o()})}),document.getElementById("delete-file").onclick=(()=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{const t=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:t,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"success-screen"}).then(()=>{storage.remove(e.fileId),location.reload()})})}),s.click(function(){p.addClass("show"),p.focus()}),p.find(".nvm").click(function(e){e.stopPropagation(),p.removeClass("show")}),p.click(function(e){e.stopPropagation()}),p.blur(()=>{p.removeClass("show")}),o()}function o(){1===document.querySelector("tbody").childNodes.length?$("#file-list").attr("hidden",!0):$("#file-list").removeAttr("hidden")}gcmCompliant().catch(e=>{$("#page-one").attr("hidden",!0),$("#unsupported-browser").removeAttr("hidden"),sendEvent("sender","unsupported",{cd6:e})}),$("#file-upload").change(e),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("sender","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#send-new-completed").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"completed"}).then(()=>{storage.referrer="completed-upload",location.href=e.currentTarget.href})}),$("#send-new-error").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"errored"}).then(()=>{storage.referrer="errored-upload",location.href=e.currentTarget.href})}),$("body").on("dragover",function(e){e.preventDefault()}).on("drop",e);const n=$("#copy-btn");n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton");const r=storage.files;if(console.log(r),0===r.length)o();else for(const e in r){const n=r[e].fileId;!function(e,n,r){const a=new XMLHttpRequest;a.onreadystatechange=(()=>{a.readyState===XMLHttpRequest.DONE&&(200===a.status?r&&t(n):404===a.status&&(storage.remove(e),0===storage.numFiles&&o()))}),a.open("get","/exists/"+e,!0),a.send()}(n,r[e],!0)}n.click(()=>{sendEvent("sender","copied",{cd4:"success-screen"});const e=document.createElement("input");e.setAttribute("value",$("#link").attr("value")),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),n.attr("disabled",!0),$("#link").attr("disabled",!0),n.html(''),window.setTimeout(()=>{n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton")},3e3)}),$(".upload-window").on("dragover",()=>{$(".upload-window").addClass("ondrag")}),$(".upload-window").on("dragleave",()=>{$(".upload-window").removeClass("ondrag")}),$("#ul-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$(".send-new").attr("href",window.location)}); +require("./common");const FileSender=require("./fileSender"),{notify:notify,gcmCompliant:gcmCompliant,findMetric:findMetric,sendEvent:sendEvent,ONE_DAY_IN_MS:ONE_DAY_IN_MS}=require("./utils"),bytes=require("bytes"),Storage=require("./storage"),storage=new Storage(localStorage),$=require("jquery");require("jquery-circle-progress");const Raven=window.Raven;storage.has("referrer")?(window.referrer=storage.referrer,storage.remove("referrer")):window.referrer="external",$(document).ready(function(){function e(o){if(o.preventDefault(),$("#page-one").attr("hidden"))return;storage.totalUploads+=1;let n="";if("drop"===o.type){if(!o.originalEvent.dataTransfer.files[0])return void $(".upload-window").removeClass("ondrag");if(o.originalEvent.dataTransfer.files.length>1||0===o.originalEvent.dataTransfer.files[0].size)return $(".upload-window").removeClass("ondrag"),void document.l10n.formatValue("uploadPageMultipleFilesAlert").then(e=>{alert(e)});n=o.originalEvent.dataTransfer.files[0]}else n=o.target.files[0];if(n.size>MAXFILESIZE)return document.l10n.formatValue("fileTooBig",{size:bytes(MAXFILESIZE)}).then(alert);$("#page-one").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#upload-progress").removeAttr("hidden"),document.l10n.formatValue("importingFile").then(e=>{$(".progress-text").text(e)}),$("body").off("drop",e);const r=new FileSender(n);$("#cancel-upload").click(()=>{r.cancel(),location.reload(),document.l10n.formatValue("uploadCancelNotification").then(e=>{notify(e)}),storage.referrer="cancelled-upload",sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"cancelled"})}),r.on("progress",e=>{const t=e[0]/e[1];$("#ul-progress").circleProgress("value",t),$("#ul-progress").circleProgress().on("circle-animation-end",function(){$(".percent-number").text(`${Math.floor(100*t)}`)}),$(".progress-text").text(`${n.name} (${bytes(e[0],{decimalPlaces:1,fixedDecimals:!0})} of ${bytes(e[1],{decimalPlaces:1})})`)}),r.on("hashing",e=>{e?document.l10n.formatValue("verifyingFile").then(e=>{$(".progress-text").text(e)}):console.log("Finished hashing")});let a;r.on("encrypting",e=>{e?document.l10n.formatValue("encryptingFile").then(e=>{$(".progress-text").text(e)}):(console.log("Finished encrypting"),a=Date.now())});let d;const l=Date.now(),s=storage.numFiles+1;sendEvent("sender","upload-started",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd5:window.referrer}),setTimeout(()=>{r.upload().then(e=>{const r=Date.now(),c=r-l,i=r-a,p=n.size/(i/1e3),u=1e3*EXPIRE_SECONDS;sendEvent("sender","upload-stopped",{cm1:n.size,cm2:c,cm3:p,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"completed"});const m={name:n.name,size:n.size,fileId:e.fileId,url:e.url,secretKey:e.secretKey,deleteToken:e.deleteToken,creationDate:new Date,expiry:u,totalTime:c,typeOfUpload:"drop"===o.type?"drop":"click",uploadSpeed:p};storage.addFile(e.fileId,m),$("#upload-filename").attr("data-l10n-id","uploadSuccessConfirmHeader"),d=window.setTimeout(()=>{$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").attr("hidden",!0),$("#share-link").removeAttr("hidden")},1e3),t(m),document.l10n.formatValue("notifyUploadDone").then(e=>{notify(e)})}).catch(e=>{0!==e&&(Raven.captureException(e),$("#page-one").attr("hidden",!0),$("#upload-progress").attr("hidden",!0),$("#upload-error").removeAttr("hidden"),window.clearTimeout(d),sendEvent("sender","upload-stopped",{cm1:n.size,cm5:storage.totalUploads,cm6:s,cm7:storage.totalDownloads,cd1:"drop"===o.type?"drop":"click",cd2:"errored",cd6:e}))})},10)}function t(e){function t(){v=h.getTime()-Date.now(),w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);let n;y>=1?(l.innerHTML=y+"h "+w%60+"m",n=window.setTimeout(()=>{t()},6e4)):0===y&&(l.innerHTML=w+"m "+E+"s",n=window.setTimeout(()=>{t()},1e3)),v<=0&&(storage.remove(e.fileId),$(l).parents("tr").remove(),window.clearTimeout(n),o())}const n=document.createElement("tr"),r=document.createElement("td"),a=document.createElement("td"),d=$("",{src:"/resources/copy-16.svg",class:"icon-copy","data-l10n-id":"copyUrlHover"}),l=document.createElement("td"),s=document.createElement("td"),c=$("",{src:"/resources/close-16.svg",class:"icon-delete","data-l10n-id":"deleteButtonHover"}),i=document.createElement("div"),p=$("
",{class:"popuptext"}),u=document.createTextNode(e.name),m=e.url.trim()+`#${e.secretKey}`.trim();$("#link").attr("value",m),$("#copy-text").attr("data-l10n-args",'{"filename": "'+e.name+'"}'),$("#copy-text").attr("data-l10n-id","copyUrlFormLabelWithName"),p.attr("tabindex","-1"),r.appendChild(u);const g=document.createElement("span");$(g).addClass("icon-cancel-1"),$(g).attr("data-l10n-id","deleteButtonHover"),s.appendChild(g);const f=document.createElement("span");$(f).addClass("icon-docs"),$(f).attr("data-l10n-id","copyUrlHover"),a.appendChild(f),a.style.color="#0A8DFF",d.click(function(){sendEvent("sender","copied",{cd4:"upload-list"});const e=document.createElement("input");e.setAttribute("value",m),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),document.l10n.formatValue("copiedUrl").then(e=>{a.innerHTML=e}),window.setTimeout(()=>{const e=document.createElement("img");$(e).addClass("icon-copy"),$(e).attr("data-l10n-id","copyUrlHover"),$(e).attr("src","/resources/copy-16.svg"),$(a).html(e)},500)}),e.creationDate=new Date(e.creationDate);const h=new Date;h.setTime(e.creationDate.getTime()+e.expiry);let v=0;v=h.getTime()-Date.now();let w=Math.floor(v/1e3/60),y=Math.floor(w/60),E=Math.floor(v/1e3%60);t(),i.classList.add("popup");const D=$("
",{class:"popup-message"});D.attr("data-l10n-id","deletePopupText");const T=$("",{class:"popup-yes"});T.attr("data-l10n-id","deletePopupYes");const C=$("",{class:"popup-no"});C.attr("data-l10n-id","deletePopupCancel"),p.html([D,T,C]),n.appendChild(r),$(a).append(d),n.appendChild(a),n.appendChild(l),$(i).append(p),$(s).append(c),s.appendChild(i),n.appendChild(s),$("tbody").append(n);const k=storage.numFiles;p.find(".popup-yes").click(t=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{$(t.target).parents("tr").remove();const n=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:n,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"upload-list"}).then(()=>{storage.remove(e.fileId)}),o()})}),document.getElementById("delete-file").onclick=(()=>{FileSender.delete(e.fileId,e.deleteToken).then(()=>{const t=ONE_DAY_IN_MS-(Date.now()-e.creationDate.getTime());sendEvent("sender","upload-deleted",{cm1:e.size,cm2:e.totalTime,cm3:e.uploadSpeed,cm4:t,cm5:storage.totalUploads,cm6:k,cm7:storage.totalDownloads,cd1:e.typeOfUpload,cd4:"success-screen"}).then(()=>{storage.remove(e.fileId),location.reload()})})}),c.click(function(){p.addClass("show"),p.focus()}),p.find(".popup-no").click(function(e){e.stopPropagation(),p.removeClass("show")}),p.click(function(e){e.stopPropagation()}),p.blur(()=>{p.removeClass("show")}),o()}function o(){1===document.querySelector("tbody").childNodes.length?$("#file-list").attr("hidden",!0):$("#file-list").removeAttr("hidden")}gcmCompliant().catch(e=>{$("#page-one").attr("hidden",!0),sendEvent("sender","unsupported",{cd6:e}).then(()=>{location.replace("/unsupported")})}),$("#file-upload").change(e),$(".legal-links a, .social-links a, #dl-firefox").click(function(e){e.preventDefault();const t=findMetric(e.currentTarget.href);sendEvent("sender","exited",{cd3:t}).then(()=>{location.href=e.currentTarget.href})}),$("#send-new-completed").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"completed"}).then(()=>{storage.referrer="completed-upload",location.href=e.currentTarget.href})}),$("#send-new-error").click(function(e){e.preventDefault(),sendEvent("sender","restarted",{cd2:"errored"}).then(()=>{storage.referrer="errored-upload",location.href=e.currentTarget.href})}),$("body").on("dragover",function(e){e.preventDefault()}).on("drop",e);const n=$("#copy-btn");n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton");const r=storage.files;if(0===r.length)o();else for(const e in r){const n=r[e].fileId;!function(e,n,r){const a=new XMLHttpRequest;a.onreadystatechange=(()=>{a.readyState===XMLHttpRequest.DONE&&(200===a.status?r&&t(n):404===a.status&&(storage.remove(e),0===storage.numFiles&&o()))}),a.open("get","/exists/"+e,!0),a.send()}(n,r[e],!0)}n.click(()=>{sendEvent("sender","copied",{cd4:"success-screen"});const e=document.createElement("input");e.setAttribute("value",$("#link").attr("value")),document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e),n.attr("disabled",!0),$("#link").attr("disabled",!0),n.html(''),window.setTimeout(()=>{n.attr("disabled",!1),$("#link").attr("disabled",!1),n.attr("data-l10n-id","copyUrlFormButton")},3e3)}),$(".upload-window").on("dragover",()=>{$(".upload-window").addClass("ondrag")}),$(".upload-window").on("dragleave",()=>{$(".upload-window").removeClass("ondrag")}),$("#ul-progress").circleProgress({value:0,startAngle:-Math.PI/2,fill:"#3B9DFF",size:158,animation:{duration:300}}),$(".send-new").attr("href",window.location)}); },{"./common":1,"./fileSender":2,"./storage":3,"./utils":5,"bytes":6,"jquery":9,"jquery-circle-progress":8}],5:[function(require,module,exports){ function arrayToHex(t){let e="";for(const n in t)t[n]<16?e+="0"+t[n].toString(16):e+=t[n].toString(16);return e}function hexToArray(t){const e=new Uint8Array(t.length/2);for(let n=0;nwindow.crypto.subtle.encrypt({name:"AES-GCM",iv:window.crypto.getRandomValues(new Uint8Array(12)),additionalData:window.crypto.getRandomValues(new Uint8Array(6)),tagLength:128},t,new ArrayBuffer(8)).then(()=>Promise.resolve()).catch(t=>Promise.reject())).catch(t=>Promise.reject())}catch(t){return Promise.reject()}}function findMetric(t){switch(t){case"https://www.mozilla.org/":return"mozilla";case"https://www.mozilla.org/about/legal":return"legal";case"https://testpilot.firefox.com/about":return"about";case"https://testpilot.firefox.com/privacy":return"privacy";case"https://testpilot.firefox.com/terms":return"terms";case"https://www.mozilla.org/en-US/privacy/websites/#cookies":return"cookies";case"https://github.com/mozilla/send":return"github";case"https://twitter.com/FxTestPilot":return"twitter";case"https://www.mozilla.org/firefox/new/?scene=2":return"download-firefox";default:return"other"}}function isFile(t){return!["referrer","totalDownloads","totalUploads","testpilot_ga__cid"].includes(t)}function sendEvent(){return window.analytics.sendEvent.apply(window.analytics,arguments).catch(()=>0)}const ONE_DAY_IN_MS=864e5;module.exports={arrayToHex:arrayToHex,hexToArray:hexToArray,notify:notify,gcmCompliant:gcmCompliant,findMetric:findMetric,isFile:isFile,sendEvent:sendEvent,ONE_DAY_IN_MS:864e5}; diff --git a/scripts/version.js b/scripts/version.js index e39e7a1a..5e112d6e 100755 --- a/scripts/version.js +++ b/scripts/version.js @@ -14,7 +14,7 @@ const filename = path.join(__dirname, '..', 'public', 'version.json'); const filedata = { commit, source: pkg.homepage, - version: pkg.version + version: process.env.CIRCLE_TAG || pkg.version }; fs.writeFileSync(filename, JSON.stringify(filedata, null, 2) + '\n'); diff --git a/server/config.js b/server/config.js index b038869e..5c6e540a 100644 --- a/server/config.js +++ b/server/config.js @@ -4,12 +4,12 @@ const conf = convict({ s3_bucket: { format: String, default: '', - env: 'P2P_S3_BUCKET' + env: 'S3_BUCKET' }, redis_host: { format: String, default: 'localhost', - env: 'P2P_REDIS_HOST' + env: 'REDIS_HOST' }, listen_port: { format: 'port', @@ -25,17 +25,27 @@ const conf = convict({ sentry_id: { format: String, default: '', - env: 'P2P_SENTRY_CLIENT' + env: 'SENTRY_CLIENT' }, sentry_dsn: { format: String, default: '', - env: 'P2P_SENTRY_DSN' + env: 'SENTRY_DSN' }, env: { format: ['production', 'development', 'test'], default: 'development', env: 'NODE_ENV' + }, + max_file_size: { + format: Number, + default: 1024 * 1024 * 1024 * 2, + env: 'MAX_FILE_SIZE' + }, + expire_seconds: { + format: Number, + default: 86400, + env: 'EXPIRE_SECONDS' } }); diff --git a/server/server.js b/server/server.js index 24e1d418..d3c10203 100644 --- a/server/server.js +++ b/server/server.js @@ -19,8 +19,6 @@ const mozlog = require('./log.js'); const log = mozlog('send.server'); const STATIC_PATH = path.join(__dirname, '../public'); -const L20N = path.join(__dirname, '../node_modules/l20n'); -const LOCALES = path.join(__dirname, '../public/locales'); const app = express(); @@ -34,10 +32,12 @@ app.engine( app.set('view engine', 'handlebars'); app.use(helmet()); -app.use(helmet.hsts({ - maxAge: 31536000, - force: conf.env === 'production' -})); +app.use( + helmet.hsts({ + maxAge: 31536000, + force: conf.env === 'production' + }) +); app.use( helmet.contentSecurityPolicy({ directives: { @@ -45,38 +45,51 @@ app.use( connectSrc: [ "'self'", 'https://sentry.prod.mozaws.net', - 'https://www.google-analytics.com', - 'https://ssl.google-analytics.com' + 'https://www.google-analytics.com' ], imgSrc: [ "'self'", - 'https://www.google-analytics.com', - 'https://ssl.google-analytics.com' + 'https://www.google-analytics.com' ], - scriptSrc: ["'self'", 'https://ssl.google-analytics.com'], + scriptSrc: ["'self'"], styleSrc: ["'self'", 'https://code.cdn.mozilla.net'], fontSrc: ["'self'", 'https://code.cdn.mozilla.net'], formAction: ["'none'"], frameAncestors: ["'none'"], - objectSrc: ["'none'"] + objectSrc: ["'none'"], + reportUri: '/__cspreport__' + } + }) +); +app.use( + busboy({ + limits: { + fileSize: conf.max_file_size } }) ); -app.use(busboy()); app.use(bodyParser.json()); app.use(express.static(STATIC_PATH)); -app.use('/l20n', express.static(L20N)); -app.use('/locales', express.static(LOCALES)); app.get('/', (req, res) => { res.render('index'); }); +app.get('/unsupported', (req, res) => { + res.render('unsupported'); +}); + +app.get('/legal', (req, res) => { + res.render('legal'); +}); + app.get('/jsconfig.js', (req, res) => { res.set('Content-Type', 'application/javascript'); res.render('jsconfig', { trackerId: conf.analytics_id, dsn: conf.sentry_id, + maxFileSize: conf.max_file_size, + expireSeconds: conf.expire_seconds, layout: false }); }); @@ -107,15 +120,17 @@ app.get('/download/:id', (req, res) => { storage .length(id) .then(contentLength => { - res.render('download', { - filename: decodeURIComponent(filename), - filesize: bytes(contentLength), - trackerId: conf.analytics_id, - dsn: conf.sentry_id + storage.ttl(id).then(timeToExpiry => { + res.render('download', { + filename: decodeURIComponent(filename), + filesize: bytes(contentLength), + sizeInBytes: contentLength, + timeToExpiry: timeToExpiry + }); }); }) .catch(() => { - res.render('download'); + res.status(404).render('notfound'); }); }); }); @@ -219,15 +234,23 @@ app.post('/upload', (req, res, next) => { req.busboy.on('file', (fieldname, file, filename) => { log.info('Uploading:', newId); - storage.set(newId, file, filename, meta).then(() => { - const protocol = conf.env === 'production' ? 'https' : req.protocol; - const url = `${protocol}://${req.get('host')}/download/${newId}/`; - res.json({ - url, - delete: meta.delete, - id: newId - }); - }); + storage.set(newId, file, filename, meta).then( + () => { + const protocol = conf.env === 'production' ? 'https' : req.protocol; + const url = `${protocol}://${req.get('host')}/download/${newId}/`; + res.json({ + url, + delete: meta.delete, + id: newId + }); + }, + err => { + if (err.message === 'limit') { + return res.sendStatus(413); + } + res.sendStatus(500); + } + ); }); req.on('close', err => { @@ -241,7 +264,7 @@ app.post('/upload', (req, res, next) => { .catch(err => { log.info('DeleteError:', newId); }); - }) + }); }); app.get('/__lbheartbeat__', (req, res) => { diff --git a/server/storage.js b/server/storage.js index 68375e47..66a1a192 100644 --- a/server/storage.js +++ b/server/storage.js @@ -23,6 +23,7 @@ if (conf.s3_bucket) { module.exports = { filename: filename, exists: exists, + ttl: ttl, length: awsLength, get: awsGet, set: awsSet, @@ -39,6 +40,7 @@ if (conf.s3_bucket) { module.exports = { filename: filename, exists: exists, + ttl: ttl, length: localLength, get: localGet, set: localSet, @@ -73,6 +75,18 @@ function metadata(id) { }); } +function ttl(id) { + return new Promise((resolve, reject) => { + redis_client.ttl(id, (err, reply) => { + if (!err) { + resolve(reply * 1000); + } else { + reject(err); + } + }); + }); +} + function filename(id) { return new Promise((resolve, reject) => { redis_client.hget(id, 'filename', (err, reply) => { @@ -129,20 +143,24 @@ function localGet(id) { function localSet(newId, file, filename, meta) { return new Promise((resolve, reject) => { - const fstream = fs.createWriteStream( - path.join(__dirname, '../static', newId) - ); + const filepath = path.join(__dirname, '../static', newId); + const fstream = fs.createWriteStream(filepath); file.pipe(fstream); - fstream.on('close', () => { + file.on('limit', () => { + file.unpipe(fstream); + fstream.destroy(new Error('limit')); + }); + fstream.on('finish', () => { redis_client.hmset(newId, meta); - redis_client.expire(newId, 86400000); + redis_client.expire(newId, conf.expire_seconds); log.info('localSet:', 'Upload Finished of ' + newId); resolve(meta.delete); }); - fstream.on('error', () => { + fstream.on('error', err => { log.error('localSet:', 'Failed upload of ' + newId); - reject(); + fs.unlinkSync(filepath); + reject(err); }); }); } @@ -211,21 +229,26 @@ function awsSet(newId, file, filename, meta) { Key: newId, Body: file }; - - return new Promise((resolve, reject) => { - s3.upload(params, function(err, _data) { - if (err) { - log.info('awsUploadError:', err.stack); // an error occurred - reject(); - } else { - redis_client.hmset(newId, meta); - - redis_client.expire(newId, 86400000); - log.info('awsUploadFinish', 'Upload Finished of ' + filename); - resolve(meta.delete); - } - }); + let hitLimit = false; + const upload = s3.upload(params); + file.on('limit', () => { + hitLimit = true; + upload.abort(); }); + return upload.promise().then( + () => { + redis_client.hmset(newId, meta); + redis_client.expire(newId, conf.expire_seconds); + log.info('awsUploadFinish', 'Upload Finished of ' + filename); + }, + err => { + if (hitLimit) { + throw new Error('limit'); + } else { + throw err; + } + } + ); } function awsDelete(id, delete_token) { diff --git a/test/.eslintrc.yml b/test/.eslintrc.yml index b65502fd..dd9f4ba4 100644 --- a/test/.eslintrc.yml +++ b/test/.eslintrc.yml @@ -19,3 +19,5 @@ rules: mocha/no-pending-tests: error mocha/no-return-and-callback: warn mocha/no-skipped-tests: error + + no-console: off # ¯\_(ツ)_/¯ diff --git a/test/unit/aws.storage.test.js b/test/unit/aws.storage.test.js index 0a1778bf..b4507e38 100644 --- a/test/unit/aws.storage.test.js +++ b/test/unit/aws.storage.test.js @@ -110,20 +110,20 @@ describe('Testing Set using aws', function() { it('Should pass when the file is successfully uploaded', function() { const buf = Buffer.alloc(10); sinon.stub(crypto, 'randomBytes').returns(buf); - s3Stub.upload.callsArgWith(1, null, {}); + s3Stub.upload.returns({promise: () => Promise.resolve()}); return storage - .set('123', {}, 'Filename.moz', {}) + .set('123', {on: sinon.stub()}, 'Filename.moz', {}) .then(() => { assert(expire.calledOnce); - assert(expire.calledWith('123', 86400000)); + assert(expire.calledWith('123', 86400)); }) .catch(err => assert.fail()); }); it('Should fail if there was an error during uploading', function() { - s3Stub.upload.callsArgWith(1, new Error(), null); + s3Stub.upload.returns({promise: () => Promise.reject()}); return storage - .set('123', {}, 'Filename.moz', 'url.com') + .set('123', {on: sinon.stub()}, 'Filename.moz', 'url.com') .then(_reply => assert.fail()) .catch(err => assert(1)); }); diff --git a/test/unit/local.storage.test.js b/test/unit/local.storage.test.js index d50fb478..48836433 100644 --- a/test/unit/local.storage.test.js +++ b/test/unit/local.storage.test.js @@ -117,12 +117,12 @@ describe('Testing Get from local filesystem', function() { describe('Testing Set to local filesystem', function() { it('Successfully writes the file to the local filesystem', function() { const stub = sinon.stub(); - stub.withArgs('close', sinon.match.any).callsArgWithAsync(1); + stub.withArgs('finish', sinon.match.any).callsArgWithAsync(1); stub.withArgs('error', sinon.match.any).returns(1); fsStub.createWriteStream.returns({ on: stub }); return storage - .set('test', { pipe: sinon.stub() }, 'Filename.moz', {}) + .set('test', { pipe: sinon.stub(), on: sinon.stub() }, 'Filename.moz', {}) .then(() => { assert(1); }) diff --git a/views/download.handlebars b/views/download.handlebars index 94cfeef8..1225bd90 100644 --- a/views/download.handlebars +++ b/views/download.handlebars @@ -1,5 +1,5 @@
- {{#if filename}} +
+ +
@@ -34,13 +36,4 @@
- {{else}} -
- - -
- - {{/if}}
diff --git a/views/index.handlebars b/views/index.handlebars index 35516497..527f54c4 100644 --- a/views/index.handlebars +++ b/views/index.handlebars @@ -1,4 +1,5 @@
+
@@ -60,7 +61,7 @@
- +
@@ -68,17 +69,5 @@
- -
- - diff --git a/views/jsconfig.handlebars b/views/jsconfig.handlebars index 1e960e83..c9ddf16d 100644 --- a/views/jsconfig.handlebars +++ b/views/jsconfig.handlebars @@ -4,3 +4,5 @@ window.dsn = '{{{dsn}}}'; {{#if trackerId}} window.trackerId = '{{{trackerId}}}'; {{/if}} +const MAXFILESIZE = {{{maxFileSize}}}; +const EXPIRE_SECONDS = {{{expireSeconds}}}; diff --git a/views/layouts/main.handlebars b/views/layouts/main.handlebars index ac886ed5..e9c8b8a8 100644 --- a/views/layouts/main.handlebars +++ b/views/layouts/main.handlebars @@ -3,30 +3,37 @@ Firefox Send - + - + - +
+ + +
{{{body}}}