diff --git a/.resinci.json b/.resinci.json index ecc07786..e2146a4d 100644 --- a/.resinci.json +++ b/.resinci.json @@ -1,6 +1,6 @@ { "node-cli": { - "node": "6.1.0", + "node": "6.14.4", "main": "lib/cli/etcher.js", "dependencies": { "linux": [ diff --git a/Makefile b/Makefile index c9cb8c80..27183969 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,6 @@ TARGET_ARCH ?= $(HOST_ARCH) # --------------------------------------------------------------------- # Electron # --------------------------------------------------------------------- - electron-develop: | $(BUILD_TEMPORARY_DIRECTORY) $(RESIN_SCRIPTS)/electron/install.sh \ -b $(shell pwd) \ @@ -159,7 +158,6 @@ TARGETS = \ lint-spell \ test-spectron \ test-gui \ - test-sdk \ test-cli \ test \ sanity-checks \ @@ -218,13 +216,11 @@ test-gui: test-sdk: electron-mocha $(MOCHA_OPTIONS) \ - tests/shared \ - tests/image-stream + tests/shared test-cli: mocha $(MOCHA_OPTIONS) \ - tests/shared \ - tests/image-stream + tests/shared test: test-gui test-sdk test-spectron diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 735ddfbc..80a3a9e0 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -40,24 +40,6 @@ to submit their work or bug reports. These are the main Etcher components, in a nutshell: -- [Etcher Image Write][etcher-image-write] - -This is the repository that implements the actual procedures to write an image -to a raw device and the place where image validation resides. Its main purpose -is to abstract the messy details of interacting with raw devices in all major -operating systems. - -- [Etcher Image Stream](../lib/image-stream) - -> (Moved from a separate repository into the main Etcher codebase) - -This module converts any kind of input into a readable stream -representing the image so it can be plugged to [etcher-image-write]. Inputs -that this module might handle could be, for example: a simple image file, a URL -to an image, a compressed image, an image inside a ZIP archive, etc. Together -with [etcher-image-write], these modules are the building blocks needed to take -an image representation to the user's device, the "Etcher's backend". - - [Drivelist](https://github.com/balena-io-modules/drivelist) As the name implies, this module's duty is to detect the connected drives @@ -106,7 +88,6 @@ since fresh eyes could help unveil things that we take for granted, but should be documented instead! [lego-blocks]: https://github.com/sindresorhus/ama/issues/10#issuecomment-117766328 -[etcher-image-write]: https://github.com/balena-io-modules/etcher-image-write [exit-codes]: https://github.com/balena-io/etcher/blob/master/lib/shared/exit-codes.js [cli-dir]: https://github.com/balena-io/etcher/tree/master/lib/cli [gui-dir]: https://github.com/balena-io/etcher/tree/master/lib/gui diff --git a/lib/cli/diskpart.js b/lib/cli/diskpart.js deleted file mode 100644 index eb6f2f97..00000000 --- a/lib/cli/diskpart.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const os = require('os') -const fs = require('fs') -const path = require('path') -const crypto = require('crypto') -const childProcess = require('child_process') -const debug = require('debug')('etcher:cli:diskpart') -const Promise = require('bluebird') -const retry = require('bluebird-retry') - -const TMP_RANDOM_BYTES = 6 -const DISKPART_DELAY = 2000 -const DISKPART_RETRIES = 5 - -/** - * @summary Generate a tmp filename with full path of OS' tmp dir - * @function - * @private - * - * @param {String} extension - temporary file extension - * @returns {String} filename - * - * @example - * const filename = tmpFilename('.sh'); - */ -const tmpFilename = (extension) => { - const random = crypto.randomBytes(TMP_RANDOM_BYTES).toString('hex') - const filename = `etcher-diskpart-${random}${extension}` - return path.join(os.tmpdir(), filename) -} - -/** - * @summary Run a diskpart script - * @param {Array} commands - list of commands to run - * @param {Function} callback - callback(error) - * @example - * runDiskpart(['rescan'], (error) => { - * ... - * }) - */ -const runDiskpart = (commands, callback) => { - if (os.platform() !== 'win32') { - callback() - return - } - - const filename = tmpFilename('') - const script = commands.join('\r\n') - - fs.writeFile(filename, script, { - mode: 0o755 - }, (writeError) => { - debug('write %s:', filename, writeError || 'OK') - - childProcess.execFile('diskpart', [ '/s', filename ], (execError, stdout, stderr) => { - debug('stdout:', stdout) - debug('stderr:', stderr) - - fs.unlink(filename, (unlinkError) => { - debug('unlink %s:', filename, unlinkError || 'OK') - callback(execError) - }) - }) - }) -} - -module.exports = { - - /** - * @summary Clean a device's partition tables - * @param {String} device - device path - * @example - * diskpart.clean('\\\\.\\PhysicalDrive2') - * .then(...) - * .catch(...) - * @returns {Promise} - */ - clean (device) { - if (os.platform() !== 'win32') { - return Promise.resolve() - } - - debug('clean', device) - - const pattern = /PHYSICALDRIVE(\d+)/i - - if (pattern.test(device)) { - const deviceId = device.match(pattern).pop() - return retry(() => { - return new Promise((resolve, reject) => { - runDiskpart([ `select disk ${deviceId}`, 'clean', 'rescan' ], (error) => { - return error ? reject(error) : resolve() - }) - }).delay(DISKPART_DELAY) - }, { - /* eslint-disable camelcase */ - max_tries: DISKPART_RETRIES - /* eslint-enable camelcase */ - }).catch((error) => { - throw new Error(`Couldn't clean the drive, ${error.failure.message} (code ${error.failure.code})`) - }) - } - - return Promise.reject(new Error(`Invalid device: "${device}"`)) - } - -} diff --git a/lib/cli/etcher.js b/lib/cli/etcher.js index 5ce0d432..60fc5b11 100644 --- a/lib/cli/etcher.js +++ b/lib/cli/etcher.js @@ -21,7 +21,8 @@ const Bluebird = require('bluebird') const visuals = require('resin-cli-visuals') const form = require('resin-cli-form') const bytes = require('pretty-bytes') -const ImageWriter = require('../sdk/writer') +const sdk = require('etcher-sdk') + const utils = require('./utils') const options = require('./options') const messages = require('../shared/messages') @@ -72,70 +73,93 @@ permissions.isElevated().then((elevated) => { }) } - const progressBars = { - write: new visuals.Progress('Flashing'), - check: new visuals.Progress('Validating') + const progressBars = new Map() + let lastStateType = null + + // eslint-disable-next-line require-jsdoc + const onProgress = (state) => { + state.message = state.active > 1 + ? `${bytes(state.totalSpeed)}/s total, ${bytes(state.speed)}/s x ${state.active}` + : `${bytes(state.totalSpeed)}/s` + + state.message = `${state.type === 'flashing' ? 'Flashing' : 'Validating'}: ${state.message}` + + // eslint-disable-next-line no-undefined + if (state.percentage === undefined) { + state.message += ` - ${bytes(state.bytes)} written` + } + + // Update progress bar + let progressBar = progressBars.get(state.type) + // eslint-disable-next-line no-undefined + if (progressBar === undefined) { + // Stop the spinner if there is one + if ((lastStateType !== null) && (lastStateType !== state.type)) { + const spinner = progressBars.get(lastStateType) + // eslint-disable-next-line no-undefined + if ((spinner !== undefined) && (spinner instanceof visuals.Spinner)) { + console.log() + spinner.stop() + } + } + // eslint-disable-next-line no-undefined + if (state.percentage === undefined) { + progressBar = new visuals.Spinner(state.message) + progressBar.start() + } else { + progressBar = new visuals.Progress(state.message) + progressBar.update(state) + } + progressBars.set(state.type, progressBar) + } else if (progressBar instanceof visuals.Spinner) { + progressBar.spinner.setSpinnerTitle(state.message) + } else { + progressBar.update(state) + } + lastStateType = state.type } - return new Bluebird((resolve, reject) => { - /** - * @summary Progress update handler - * @param {Object} state - progress state - * @private - * @example - * writer.on('progress', onProgress) - */ - const onProgress = (state) => { - state.message = state.active > 1 - ? `${bytes(state.totalSpeed)}/s total, ${bytes(state.speed)}/s x ${state.active}` - : `${bytes(state.totalSpeed)}/s` - - state.message = `${state.type === 'write' ? 'Flashing' : 'Validating'}: ${state.message}` - - // Update progress bar - progressBars[state.type].update(state) - } - - const writer = new ImageWriter({ - verify: options.check, - unmountOnSuccess: options.unmount, - checksumAlgorithms: options.check ? [ 'xxhash' ] : [] - }) - - /** - * @summary Finish handler - * @private - * @example - * writer.on('finish', onFinish) - */ - const onFinish = function () { - resolve(Array.from(writer.destinations.values())) - } - - writer.on('progress', onProgress) - writer.on('error', reject) - writer.on('finish', onFinish) - - // NOTE: Drive can be (String|Array) - const destinations = [].concat(answers.drive) - - writer.write(imagePath, destinations) + const adapter = new sdk.scanner.adapters.BlockDeviceAdapter(() => { + return options.unmount }) -}).then((results) => { + const scanner = new sdk.scanner.Scanner([ adapter ]) + return new Promise((resolve, reject) => { + scanner.on('ready', resolve) + scanner.on('error', reject) + scanner.start() + }) + .then(() => { + return (new sdk.sourceDestination.File(imagePath, sdk.sourceDestination.File.OpenFlags.Read)).getInnerSource() + }) + .then((innerSource) => { + // NOTE: Drive can be (String|Array) + const destinations = _.map([].concat(answers.drive), (device) => { + const drive = scanner.getBy('device', device) + if (!drive) { + throw new Error(`No such drive ${device}`) + } + return drive + }) + return sdk.multiWrite.pipeSourceToDestinations( + innerSource, + destinations, + (destination, error) => { + console.log(`Error "${error}" on ${destination.drive}`) + }, + onProgress, + options.check + ) + }) +}).then(({ failures, bytesWritten }) => { let exitCode = EXIT_CODES.SUCCESS - if (options.check) { + if (failures.size > 0) { + exitCode = EXIT_CODES.GENERAL_ERROR console.log('') - console.log('Checksums:') - _.forEach(results, (result) => { - if (result.error) { - exitCode = EXIT_CODES.GENERAL_ERROR - console.log(` - ${result.device.device}: ${result.error.message}`) - } else { - console.log(` - ${result.device.device}: ${result.checksum.xxhash}`) - } - }) + for (const [ destination, error ] of failures) { + console.log(` - ${destination.drive}: ${error.message}`) + } } process.exit(exitCode) diff --git a/lib/gui/app/app.js b/lib/gui/app/app.js index f39bb935..8b19e4af 100644 --- a/lib/gui/app/app.js +++ b/lib/gui/app/app.js @@ -28,8 +28,11 @@ var angular = require('angular') const electron = require('electron') const Bluebird = require('bluebird') +const sdk = require('etcher-sdk') +const _ = require('lodash') const semver = require('semver') const uuidV4 = require('uuid/v4') + const EXIT_CODES = require('../../shared/exit-codes') const messages = require('../../shared/messages') const s3Packages = require('../../shared/s3-packages') @@ -229,29 +232,160 @@ app.run(() => { }) }) +/** + * @summary The radix used by USB ID numbers + * @type {Number} + * @constant + */ +const USB_ID_RADIX = 16 + +/** + * @summary The expected length of a USB ID number + * @type {Number} + * @constant + */ +const USB_ID_LENGTH = 4 + +/** + * @summary Convert a USB id (e.g. product/vendor) to a string + * @function + * @private + * + * @param {Number} id - USB id + * @returns {String} string id + * + * @example + * console.log(usbIdToString(2652)) + * > '0x0a5c' + */ +const usbIdToString = (id) => { + return `0x${_.padStart(id.toString(USB_ID_RADIX), USB_ID_LENGTH, '0')}` +} + +/** + * @summary Product ID of BCM2708 + * @type {Number} + * @constant + */ +const USB_PRODUCT_ID_BCM2708_BOOT = 0x2763 + +/** + * @summary Product ID of BCM2710 + * @type {Number} + * @constant + */ +const USB_PRODUCT_ID_BCM2710_BOOT = 0x2764 + +/** + * @summary Compute module descriptions + * @type {Object} + * @constant + */ +const COMPUTE_MODULE_DESCRIPTIONS = { + [USB_PRODUCT_ID_BCM2708_BOOT]: 'Compute Module 1', + [USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3' +} + app.run(($timeout) => { - driveScanner.on('devices', (drives) => { - const BLACKLISTED_DRIVES = settings.has('driveBlacklist') - ? settings.get('driveBlacklist').split(',') - : [] + const BLACKLISTED_DRIVES = settings.has('driveBlacklist') + ? settings.get('driveBlacklist').split(',') + : [] + + // eslint-disable-next-line require-jsdoc + const driveIsAllowed = (drive) => { + return !( + BLACKLISTED_DRIVES.includes(drive.devicePath) || + BLACKLISTED_DRIVES.includes(drive.device) || + BLACKLISTED_DRIVES.includes(drive.raw) + ) + } + + // eslint-disable-next-line require-jsdoc,consistent-return + const prepareDrive = (drive) => { + if (drive instanceof sdk.sourceDestination.BlockDevice) { + return drive.drive + } else if (drive instanceof sdk.sourceDestination.UsbbootDrive) { + // This is a workaround etcher expecting a device string and a size + drive.device = drive.usbDevice.portId + drive.size = null + drive.progress = 0 + drive.disabled = true + drive.on('progress', (progress) => { + updateDriveProgress(drive, progress) + }) + return drive + } else if (drive instanceof sdk.sourceDestination.DriverlessDevice) { + const description = COMPUTE_MODULE_DESCRIPTIONS[drive.deviceDescriptor.idProduct] || 'Compute Module' + return { + device: `${usbIdToString(drive.deviceDescriptor.idVendor)}:${usbIdToString(drive.deviceDescriptor.idProduct)}`, + displayName: 'Missing drivers', + description, + mountpoints: [], + isReadOnly: false, + isSystem: false, + disabled: true, + icon: 'warning', + size: null, + link: 'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md', + linkCTA: 'Install', + linkTitle: 'Install missing drivers', + linkMessage: [ + 'Would you like to download the necessary drivers from the Raspberry Pi Foundation?', + 'This will open your browser.\n\n', + 'Once opened, download and run the installer from the "Windows Installer" section to install the drivers.' + ].join(' ') + } + } + } + + // eslint-disable-next-line require-jsdoc + const setDrives = (drives) => { + availableDrives.setDrives(_.values(drives)) // Safely trigger a digest cycle. // In some cases, AngularJS doesn't acknowledge that the // available drives list has changed, and incorrectly // keeps asking the user to "Connect a drive". - $timeout(() => { - if (BLACKLISTED_DRIVES.length) { - const allowedDrives = drives.filter((drive) => { - return !(BLACKLISTED_DRIVES.includes(drive.devicePath) || - BLACKLISTED_DRIVES.includes(drive.device) || - BLACKLISTED_DRIVES.includes(drive.raw)) - }) - availableDrives.setDrives(allowedDrives) - } else { - availableDrives.setDrives(drives) - } - }) - }) + $timeout() + } + + // eslint-disable-next-line require-jsdoc + const getDrives = () => { + return _.keyBy(availableDrives.getDrives() || [], 'device') + } + + // eslint-disable-next-line require-jsdoc + const addDrive = (drive) => { + const preparedDrive = prepareDrive(drive) + if (!driveIsAllowed(preparedDrive)) { + return + } + const drives = getDrives() + drives[preparedDrive.device] = preparedDrive + setDrives(drives) + } + + // eslint-disable-next-line require-jsdoc + const removeDrive = (drive) => { + const preparedDrive = prepareDrive(drive) + const drives = getDrives() + // eslint-disable-next-line prefer-reflect + delete drives[preparedDrive.device] + setDrives(drives) + } + + // eslint-disable-next-line require-jsdoc + const updateDriveProgress = (drive, progress) => { + const drives = getDrives() + const driveInMap = drives[drive.device] + if (driveInMap) { + driveInMap.progress = progress + setDrives(drives) + } + } + + driveScanner.on('attach', addDrive) + driveScanner.on('detach', removeDrive) driveScanner.on('error', (error) => { // Stop the drive scanning loop in case of errors, diff --git a/lib/gui/app/components/file-selector/file-selector/file-selector.jsx b/lib/gui/app/components/file-selector/file-selector/file-selector.jsx index 4cbcffb8..e456aa5f 100644 --- a/lib/gui/app/components/file-selector/file-selector/file-selector.jsx +++ b/lib/gui/app/components/file-selector/file-selector/file-selector.jsx @@ -17,6 +17,7 @@ 'use strict' const path = require('path') +const sdk = require('etcher-sdk') const Bluebird = require('bluebird') const React = require('react') @@ -36,7 +37,6 @@ const osDialog = require('../../../os/dialog') const exceptionReporter = require('../../../modules/exception-reporter') const messages = require('../../../../../shared/messages') const errors = require('../../../../../shared/errors') -const imageStream = require('../../../../../sdk/image-stream') const supportedFormats = require('../../../../../shared/supported-formats') const analytics = require('../../../modules/analytics') @@ -174,7 +174,7 @@ class FileSelector extends React.PureComponent { if (!supportedFormats.isSupportedImage(image.path)) { const invalidImageError = errors.createUserError({ title: 'Invalid image', - description: messages.error.invalidImage(image) + description: messages.error.invalidImage(image.path) }) osDialog.showError(invalidImageError) @@ -183,7 +183,7 @@ class FileSelector extends React.PureComponent { applicationSessionUuid: store.getState().toJS().applicationSessionUuid, flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid }) - return + return Bluebird.resolve() } return Bluebird.try(() => { @@ -229,7 +229,7 @@ class FileSelector extends React.PureComponent { // An easy way so we can quickly identify if we're making use of // certain features without printing pages of text to DevTools. image.logo = Boolean(image.logo) - image.bmap = Boolean(image.bmap) + image.blockMap = Boolean(image.blockMap) analytics.logEvent('Select image', { image, @@ -247,23 +247,47 @@ class FileSelector extends React.PureComponent { return } + if (!supportedFormats.isSupportedImage(file.path)) { + const invalidImageError = errors.createUserError({ + title: 'Invalid image', + description: messages.error.invalidImage(file.path) + }) + + osDialog.showError(invalidImageError) + analytics.logEvent('Invalid image', { path: file.path }) + return + } + debug('FileSelector:getImageMetadata', file) - imageStream.getImageMetadata(file.path) + const source = new sdk.sourceDestination.File(file.path, sdk.sourceDestination.File.OpenFlags.Read) + source.getInnerSource() + .then((innerSource) => { + return innerSource.getMetadata() .then((imageMetadata) => { debug('FileSelector:getImageMetadata', imageMetadata) - return this.selectImage(imageMetadata) - }) - .catch((error) => { - debug('FileSelector:getImageMetadata', error) - const imageError = errors.createUserError({ - title: 'Error opening image', - description: messages.error.openImage(path.basename(file.path), error.message) + imageMetadata.path = file.path + imageMetadata.extension = path.extname(file.path).slice(1) + return innerSource.getPartitionTable() + .then((partitionTable) => { + if (partitionTable !== undefined) { + imageMetadata.hasMBR = true + imageMetadata.partitions = partitionTable.partitions + } + return this.selectImage(imageMetadata) }) - - osDialog.showError(imageError) - analytics.logException(error) }) + }) + .catch((error) => { + debug('FileSelector:getImageMetadata', error) + const imageError = errors.createUserError({ + title: 'Error opening image', + description: messages.error.openImage(path.basename(file.path), error.message) + }) + + osDialog.showError(imageError) + analytics.logException(error) + }) } onHighlight (file) { diff --git a/lib/gui/app/models/selection-state.js b/lib/gui/app/models/selection-state.js index 56af929d..180e209f 100644 --- a/lib/gui/app/models/selection-state.js +++ b/lib/gui/app/models/selection-state.js @@ -93,13 +93,9 @@ exports.deselectOtherDrives = (driveDevice) => { * @example * selectionState.selectImage({ * path: 'foo.img', - * size: { - * original: 1000000000, - * final: { - * estimation: false, - * value: 1000000000 - * } - * } + * size: 1000000000, + * compressedSize: 1000000000, + * isSizeEstimated: false, * }); */ exports.selectImage = (image) => { @@ -211,9 +207,7 @@ exports.getImageSize = () => { return _.get(store.getState().toJS(), [ 'selection', 'image', - 'size', - 'final', - 'value' + 'size' ]) } diff --git a/lib/gui/app/models/store.js b/lib/gui/app/models/store.js index 2c7123b5..4a9f2247 100644 --- a/lib/gui/app/models/store.js +++ b/lib/gui/app/models/store.js @@ -56,8 +56,6 @@ const verifyNoNilFields = (object, fields, name) => { * @private */ const flashStateNoNilFields = [ - 'percentage', - 'eta', 'speed', 'totalSpeed' ] @@ -69,8 +67,7 @@ const flashStateNoNilFields = [ */ const selectImageNoNilFields = [ 'path', - 'extension', - 'size' + 'extension' ] /** @@ -94,8 +91,8 @@ const DEFAULT_STATE = Immutable.fromJS({ successful: 0, failed: 0, percentage: 0, - speed: 0, - totalSpeed: 0 + speed: null, + totalSpeed: null } }) @@ -165,7 +162,7 @@ const storeReducer = (state = DEFAULT_STATE, action) => { const drives = action.data - if (!_.isArray(drives) || !_.every(drives, _.isPlainObject)) { + if (!_.isArray(drives) || !_.every(drives, _.isObject)) { throw errors.createError({ title: `Invalid drives: ${drives}` }) @@ -254,13 +251,13 @@ const storeReducer = (state = DEFAULT_STATE, action) => { }) } - if (!utils.isValidPercentage(action.data.percentage)) { + if (!_.isUndefined(action.data.percentage) && !utils.isValidPercentage(action.data.percentage)) { throw errors.createError({ title: `Invalid state percentage: ${action.data.percentage}` }) } - if (!_.isNumber(action.data.eta)) { + if (!_.isUndefined(action.data.eta) && !_.isNumber(action.data.eta)) { throw errors.createError({ title: `Invalid state eta: ${action.data.eta}` }) @@ -385,64 +382,62 @@ const storeReducer = (state = DEFAULT_STATE, action) => { }) } - if (_.some([ - !_.isString(action.data.extension), - !_.includes(supportedFormats.getAllExtensions(), action.data.extension) - ])) { + if (!_.isString(action.data.extension)) { throw errors.createError({ title: `Invalid image extension: ${action.data.extension}` }) } - const lastImageExtension = fileExtensions.getLastFileExtension(action.data.path) + const extension = _.toLower(action.data.extension) - if (lastImageExtension !== action.data.extension) { - if (!action.data.archiveExtension) { + if (!_.includes(supportedFormats.getAllExtensions(), extension)) { + throw errors.createError({ + title: `Invalid image extension: ${action.data.extension}` + }) + } + + let lastImageExtension = fileExtensions.getLastFileExtension(action.data.path) + lastImageExtension = _.isString(lastImageExtension) ? _.toLower(lastImageExtension) : lastImageExtension + + if (lastImageExtension !== extension) { + if (!_.isString(action.data.archiveExtension)) { throw errors.createError({ title: 'Missing image archive extension' }) } - if (_.some([ - !_.isString(action.data.archiveExtension), - !_.includes(supportedFormats.getAllExtensions(), action.data.archiveExtension) - ])) { + const archiveExtension = _.toLower(action.data.archiveExtension) + + if (!_.includes(supportedFormats.getAllExtensions(), archiveExtension)) { throw errors.createError({ title: `Invalid image archive extension: ${action.data.archiveExtension}` }) } - if (lastImageExtension !== action.data.archiveExtension) { + if (lastImageExtension !== archiveExtension) { throw errors.createError({ title: `Image archive extension mismatch: ${action.data.archiveExtension} and ${lastImageExtension}` }) } } - if (!_.isPlainObject(action.data.size)) { - throw errors.createError({ - title: `Invalid image size: ${action.data.size}` - }) - } - const MINIMUM_IMAGE_SIZE = 0 - if (!_.isInteger(action.data.size.original) || action.data.size.original < MINIMUM_IMAGE_SIZE) { - throw errors.createError({ - title: `Invalid original image size: ${action.data.size.original}` - }) + // eslint-disable-next-line no-undefined + if (action.data.size !== undefined) { + if ((action.data.size < MINIMUM_IMAGE_SIZE) || !_.isInteger(action.data.size)) { + throw errors.createError({ + title: `Invalid image size: ${action.data.size}` + }) + } } - if (!_.isInteger(action.data.size.final.value) || action.data.size.final.value < MINIMUM_IMAGE_SIZE) { - throw errors.createError({ - title: `Invalid final image size: ${action.data.size.final.value}` - }) - } - - if (!_.isBoolean(action.data.size.final.estimation)) { - throw errors.createError({ - title: `Invalid final image size estimation flag: ${action.data.size.final.estimation}` - }) + if (!_.isUndefined(action.data.compressedSize)) { + if ((action.data.compressedSize < MINIMUM_IMAGE_SIZE) || !_.isInteger(action.data.compressedSize)) { + throw errors.createError({ + title: `Invalid image compressed size: ${action.data.compressedSize}` + }) + } } if (action.data.url && !_.isString(action.data.url)) { diff --git a/lib/gui/app/modules/drive-scanner.js b/lib/gui/app/modules/drive-scanner.js index 01fa9757..614349e6 100644 --- a/lib/gui/app/modules/drive-scanner.js +++ b/lib/gui/app/modules/drive-scanner.js @@ -16,16 +16,39 @@ 'use strict' -const settings = require('../models/settings') -const SDK = require('../../../sdk') +const sdk = require('etcher-sdk') +const process = require('process') -const scanner = SDK.createScanner({ - blockdevice: { - get includeSystemDrives () { - return settings.get('unsafeMode') && !settings.get('disableUnsafeMode') - } - }, - usbboot: {} -}) +const settings = require('../models/settings') + +/** + * @summary returns true if system drives should be shown + * @function + * + * @returns {Boolean} + * + * @example + * const shouldInclude = includeSystemDrives() + */ +const includeSystemDrives = () => { + return settings.get('unsafeMode') && !settings.get('disableUnsafeMode') +} + +const adapters = [ + new sdk.scanner.adapters.BlockDeviceAdapter(includeSystemDrives) +] + +// Can't use permissions.isElevated() here as it returns a promise and we need to set +// module.exports = scanner right now. +// eslint-disable-next-line no-magic-numbers +if ((process.platform !== 'linux') || (process.geteuid() === 0)) { + adapters.push(new sdk.scanner.adapters.UsbbootDeviceAdapter()) +} + +if (process.platform === 'win32') { + adapters.push(new sdk.scanner.adapters.DriverlessDeviceAdapter()) +} + +const scanner = new sdk.scanner.Scanner(adapters) module.exports = scanner diff --git a/lib/gui/app/modules/progress-status.js b/lib/gui/app/modules/progress-status.js index 57d60536..de0875fa 100644 --- a/lib/gui/app/modules/progress-status.js +++ b/lib/gui/app/modules/progress-status.js @@ -18,6 +18,7 @@ const settings = require('../models/settings') const utils = require('../../../shared/utils') +const units = require('../../../shared/units') /** * @summary Make the progress status subtitle string @@ -58,7 +59,11 @@ exports.fromFlashState = (state) => { return 'Finishing...' } else if (isFlashing) { - return `${state.percentage}% Flashing` + // eslint-disable-next-line no-eq-null + if (state.percentage != null) { + return `${state.percentage}% Flashing` + } + return `${units.bytesToClosestUnit(state.position)} flashed` } else if (isValidating) { return `${state.percentage}% Validating` } else if (!isFlashing && !isValidating) { diff --git a/lib/gui/app/os/window-progress.js b/lib/gui/app/os/window-progress.js index dcc8bccf..2efb4c1e 100644 --- a/lib/gui/app/os/window-progress.js +++ b/lib/gui/app/os/window-progress.js @@ -90,7 +90,10 @@ exports.currentWindow = electron.remote.getCurrentWindow() * }) */ exports.set = (state) => { - exports.currentWindow.setProgressBar(utils.percentageToFloat(state.percentage)) + // eslint-disable-next-line no-eq-null + if (state.percentage !== null) { + exports.currentWindow.setProgressBar(utils.percentageToFloat(state.percentage)) + } exports.currentWindow.setTitle(getWindowTitle(state)) } diff --git a/lib/gui/app/pages/main/controllers/flash.js b/lib/gui/app/pages/main/controllers/flash.js index 991e14ee..0a98852c 100644 --- a/lib/gui/app/pages/main/controllers/flash.js +++ b/lib/gui/app/pages/main/controllers/flash.js @@ -110,13 +110,9 @@ module.exports = function ( * @example * FlashController.flashImageToDrive({ * path: 'rpi.img', - * size: { - * original: 1000000000, - * final: { - * estimation: false, - * value: 1000000000 - * } - * } + * size: 1000000000, + * compressedSize: 1000000000, + * isSizeEstimated: false, * }, [ * '/dev/disk2', * '/dev/disk5' @@ -149,7 +145,7 @@ module.exports = function ( // otherwise Windows throws EPERM driveScanner.stop() - return imageWriter.flash(image.path, devices) + return imageWriter.flash(image.path, drives) }).then(() => { if (!flashState.wasLastFlashCancelled()) { const flashResults = flashState.getFlashResults() @@ -188,6 +184,7 @@ module.exports = function ( exceptionReporter.report(error) } }).finally(() => { + availableDrives.setDrives([]) driveScanner.start() unsubscribe() }) diff --git a/lib/gui/app/pages/main/controllers/image-selection.js b/lib/gui/app/pages/main/controllers/image-selection.js index 38f12901..eeb3f2b7 100644 --- a/lib/gui/app/pages/main/controllers/image-selection.js +++ b/lib/gui/app/pages/main/controllers/image-selection.js @@ -19,10 +19,11 @@ const _ = require('lodash') const Bluebird = require('bluebird') const path = require('path') +const sdk = require('etcher-sdk') + const store = require('../../../models/store') const messages = require('../../../../../shared/messages') const errors = require('../../../../../shared/errors') -const imageStream = require('../../../../../sdk/image-stream') const supportedFormats = require('../../../../../shared/supported-formats') const analytics = require('../../../modules/analytics') const settings = require('../../../models/settings') @@ -124,7 +125,7 @@ module.exports = function ( // An easy way so we can quickly identify if we're making use of // certain features without printing pages of text to DevTools. image.logo = Boolean(image.logo) - image.bmap = Boolean(image.bmap) + image.blockMap = Boolean(image.blockMap) return analytics.logEvent('Select image', { image, @@ -145,20 +146,47 @@ module.exports = function ( * ImageSelectionController.selectImageByPath('path/to/image.img'); */ this.selectImageByPath = (imagePath) => { - imageStream.getImageMetadata(imagePath) - .then((imageMetadata) => { - $timeout(() => { - this.selectImage(imageMetadata) - }) + if (!supportedFormats.isSupportedImage(imagePath)) { + const invalidImageError = errors.createUserError({ + title: 'Invalid image', + description: messages.error.invalidImage(imagePath) }) - .catch((error) => { - const imageError = errors.createUserError({ - title: 'Error opening image', - description: messages.error.openImage(path.basename(imagePath), error.message) - }) - osDialog.showError(imageError) - analytics.logException(error) + osDialog.showError(invalidImageError) + analytics.logEvent('Invalid image', { path: imagePath }) + return + } + + const source = new sdk.sourceDestination.File(imagePath, sdk.sourceDestination.File.OpenFlags.Read) + source.getInnerSource() + .then((innerSource) => { + return innerSource.getMetadata() + .then((metadata) => { + return innerSource.getPartitionTable() + .then((partitionTable) => { + if (partitionTable) { + metadata.hasMBR = true + metadata.partitions = partitionTable.partitions + } + metadata.path = imagePath + // eslint-disable-next-line no-magic-numbers + metadata.extension = path.extname(imagePath).slice(1) + this.selectImage(metadata) + $timeout() + }) + }) + .catch((error) => { + const imageError = errors.createUserError({ + title: 'Error opening image', + description: messages.error.openImage(path.basename(imagePath), error.message) + }) + osDialog.showError(imageError) + analytics.logException(error) + }) + .then(() => { + return innerSource.close() + .catch(_.noop) + }) }) } diff --git a/lib/gui/app/pages/main/templates/main.tpl.html b/lib/gui/app/pages/main/templates/main.tpl.html index 684f8a60..d48fe362 100644 --- a/lib/gui/app/pages/main/templates/main.tpl.html +++ b/lib/gui/app/pages/main/templates/main.tpl.html @@ -119,7 +119,7 @@
-
diff --git a/lib/gui/modules/child-writer.js b/lib/gui/modules/child-writer.js index db28aa25..bead21f8 100644 --- a/lib/gui/modules/child-writer.js +++ b/lib/gui/modules/child-writer.js @@ -18,9 +18,9 @@ const _ = require('lodash') const ipc = require('node-ipc') +const sdk = require('etcher-sdk') const EXIT_CODES = require('../../shared/exit-codes') const errors = require('../../shared/errors') -const ImageWriter = require('../../sdk/writer') ipc.config.id = process.env.IPC_CLIENT_ID ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT @@ -85,6 +85,49 @@ const handleError = (error) => { terminate(EXIT_CODES.GENERAL_ERROR) } +/** + * @summary writes the source to the destinations and valiates the writes + * @param {SourceDestination} source - source + * @param {SourceDestination[]} destinations - destinations + * @param {Boolean} verify - whether to validate the writes or not + * @param {Function} onProgress - function to call on progress + * @param {Function} onFail - function to call on fail + * @param {Function} onFinish - function to call on finish + * @param {Function} onError - function to call on error + * @returns {Promise} + * + * @example + * writeAndValidate(source, destinations, verify, onProgress, onFail, onFinish, onError) + */ +const writeAndValidate = (source, destinations, verify, onProgress, onFail, onFinish, onError) => { + return source.getInnerSource() + .then((innerSource) => { + return sdk.multiWrite.pipeSourceToDestinations( + innerSource, + destinations, + onFail, + onProgress, + verify + ) + }) + .then(({ failures, bytesWritten }) => { + const result = { + bytesWritten, + devices: { + failed: failures.size, + successful: destinations.length - failures.size + }, + errors: [] + } + for (const [ destination, error ] of failures) { + error.device = destination.drive.device + result.errors.push(error) + } + onFinish(result) + }) + .catch(onError) +} + ipc.connectTo(IPC_SERVER_ID, () => { process.once('uncaughtException', handleError) @@ -111,18 +154,7 @@ ipc.connectTo(IPC_SERVER_ID, () => { terminate(EXIT_CODES.SUCCESS) }) - let writer = null - ipc.of[IPC_SERVER_ID].on('write', (options) => { - const destinations = [].concat(options.destinations) - - log(`Image: ${options.imagePath}`) - log(`Devices: ${destinations.join(', ')}`) - log(`Umount on success: ${options.unmountOnSuccess}`) - log(`Validate on success: ${options.validateWriteOnSuccess}`) - - let exitCode = EXIT_CODES.SUCCESS - /** * @summary Progress handler * @param {Object} state - progress state @@ -133,6 +165,8 @@ ipc.connectTo(IPC_SERVER_ID, () => { ipc.of[IPC_SERVER_ID].emit('state', state) } + let exitCode = EXIT_CODES.SUCCESS + /** * @summary Finish handler * @param {Object} results - Flash results @@ -159,6 +193,8 @@ ipc.connectTo(IPC_SERVER_ID, () => { terminate(exitCode) } + ipc.of[IPC_SERVER_ID].on('cancel', onAbort) + /** * @summary Error handler * @param {Error} error - error @@ -173,36 +209,38 @@ ipc.connectTo(IPC_SERVER_ID, () => { /** * @summary Failure handler (non-fatal errors) - * @param {Object} event - event data (error & device) + * @param {SourceDestination} destination - destination + * @param {Error} error - error * @example * writer.on('fail', onFail) */ - const onFail = (event) => { + const onFail = (destination, error) => { ipc.of[IPC_SERVER_ID].emit('fail', { - device: event.device, - error: errors.toJSON(event.error) + // TODO: device should be destination + device: destination.drive, + error: errors.toJSON(error) }) } - writer = new ImageWriter({ - verify: options.validateWriteOnSuccess, - unmountOnSuccess: options.unmountOnSuccess, - checksumAlgorithms: options.checksumAlgorithms || [] + const destinations = _.map(options.destinations, 'device') + const dests = _.map(options.destinations, (destination) => { + return new sdk.sourceDestination.BlockDevice(destination, options.unmountOnSuccess) }) + const source = new sdk.sourceDestination.File(options.imagePath, sdk.sourceDestination.File.OpenFlags.Read) + writeAndValidate( + source, + dests, + options.validateWriteOnSuccess, + onProgress, + onFail, + onFinish, + onError + ) - writer.on('error', onError) - writer.on('fail', onFail) - writer.on('progress', onProgress) - writer.on('finish', onFinish) - writer.on('abort', onAbort) - - writer.write(options.imagePath, destinations) - }) - - ipc.of[IPC_SERVER_ID].on('cancel', () => { - if (writer) { - writer.abort() - } + log(`Image: ${options.imagePath}`) + log(`Devices: ${destinations.join(', ')}`) + log(`Umount on success: ${options.unmountOnSuccess}`) + log(`Validate on success: ${options.validateWriteOnSuccess}`) }) ipc.of[IPC_SERVER_ID].on('connect', () => { diff --git a/lib/sdk/adapters/blockdevice/index.js b/lib/sdk/adapters/blockdevice/index.js deleted file mode 100644 index ad462b30..00000000 --- a/lib/sdk/adapters/blockdevice/index.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const EventEmitter = require('events') -const drivelist = Bluebird.promisifyAll(require('drivelist')) - -const USBBOOT_RPI_COMPUTE_MODULE_NAMES = [ - '0001', - 'RPi-MSD- 0001', - 'File-Stor Gadget', - 'Linux File-Stor Gadget USB Device', - 'Linux File-Stor Gadget Media' -] - -/** - * @summary BlockDeviceAdapter - * @class - */ -class BlockDeviceAdapter extends EventEmitter { - /** - * @summary BlockDeviceAdapter constructor - * @class - * @example - * const adapter = new BlockDeviceAdapter() - */ - constructor () { - super() - - /** @type {String} Adapter name */ - this.id = this.constructor.id - - this.devices = [] - this.on('devices', (devices) => { - this.devices = devices - }) - } - - /** - * @summary Scan for block devices - * @public - * - * @param {Object} [options] - options - * @param {Object} [options.includeSystemDrives=false] - include system drives - * @param {Function} [callback] - optional callback - * @returns {BlockDeviceAdapter} - * - * @example - * adapter.scan({ - * includeSystemDrives: true - * }, (error, devices) => { - * // ... - * }) - */ - scan (options = {}, callback) { - // eslint-disable-next-line lodash/prefer-lodash-method - drivelist.listAsync().map((drive) => { - drive.adapter = this.id - - // TODO: Find a better way to detect that a certain - // block device is a compute module initialized - // through usbboot. - if (_.includes(USBBOOT_RPI_COMPUTE_MODULE_NAMES, drive.description)) { - drive.description = 'Compute Module' - drive.icon = 'raspberrypi' - drive.isSystem = false - } - - return drive - }).catch((error) => { - this.emit('error', error) - callback && callback(error) - }).filter((drive) => { - // Always ignore RAID attached devices, as they are in danger-country; - // Even flashing RAIDs intentionally can have unintended effects - if (drive.busType === 'RAID') { - return false - } - return !drive.error && Number.isFinite(drive.size) && (options.includeSystemDrives || !drive.isSystem) - }).map((drive) => { - drive.displayName = drive.device - if (/PhysicalDrive/i.test(drive.device) && drive.mountpoints.length) { - drive.displayName = _.map(drive.mountpoints, 'path').join(', ') - } - return drive - }).then((drives) => { - this.emit('devices', drives) - callback && callback(null, drives) - }) - - return this - } -} - -/** - * @summary The name of this adapter - * @public - * @type {String} - * @constant - */ -BlockDeviceAdapter.id = 'blockdevice' - -// Exports -module.exports = BlockDeviceAdapter diff --git a/lib/sdk/adapters/index.js b/lib/sdk/adapters/index.js deleted file mode 100644 index 79bc682a..00000000 --- a/lib/sdk/adapters/index.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const os = require('os') - -const permissions = require('../../shared/permissions') - -/** - * @summary The list of loaded adapters - * @type {Object[]} - * @constant - */ -const ADAPTERS = [ - require('./blockdevice') -] - -// On GNU/Linux, we only support usbboot when running as root. -if ((os.platform() !== 'linux') || permissions.isElevatedUnixSync()) { - ADAPTERS.push(require('./usbboot')) -} - -/** - * @summary Initialised adapters - * @type {Object} - * @constant - */ -module.exports = _.reduce(ADAPTERS, (adapters, Adapter) => { - adapters[Adapter.id] = new Adapter() - return adapters -}, {}) diff --git a/lib/sdk/adapters/usbboot/blobs/LICENSE b/lib/sdk/adapters/usbboot/blobs/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/lib/sdk/adapters/usbboot/blobs/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-0-w.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-0-w.dtb deleted file mode 100644 index 494cb1a5..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-0-w.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b-plus.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b-plus.dtb deleted file mode 100644 index 409f5698..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b-plus.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b.dtb deleted file mode 100644 index 827f33fd..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-b.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-cm.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-cm.dtb deleted file mode 100644 index 4989c3c4..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2708-rpi-cm.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2709-rpi-2-b.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2709-rpi-2-b.dtb deleted file mode 100644 index 9c18cb06..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2709-rpi-2-b.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-3-b.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-3-b.dtb deleted file mode 100644 index def1bc3d..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-3-b.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-cm3.dtb b/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-cm3.dtb deleted file mode 100644 index f7725a3a..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/bcm2710-rpi-cm3.dtb and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/config.txt b/lib/sdk/adapters/usbboot/blobs/config.txt deleted file mode 100644 index b59d4091..00000000 --- a/lib/sdk/adapters/usbboot/blobs/config.txt +++ /dev/null @@ -1,4 +0,0 @@ -gpu_mem=16 -dtoverlay=dwc2,dr_mode=peripheral -dtparam=act_led_trigger=none -dtparam=act_led_activelow=off diff --git a/lib/sdk/adapters/usbboot/blobs/kernel.img b/lib/sdk/adapters/usbboot/blobs/kernel.img deleted file mode 100644 index 2011bdbe..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/kernel.img and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/kernel7.img b/lib/sdk/adapters/usbboot/blobs/kernel7.img deleted file mode 100644 index d050ef30..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/kernel7.img and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/overlays/dwc2.dtbo b/lib/sdk/adapters/usbboot/blobs/overlays/dwc2.dtbo deleted file mode 100644 index aa85a4e7..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/overlays/dwc2.dtbo and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/raspberrypi/LICENSE b/lib/sdk/adapters/usbboot/blobs/raspberrypi/LICENSE deleted file mode 100644 index 89b5c0c0..00000000 --- a/lib/sdk/adapters/usbboot/blobs/raspberrypi/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2006, Broadcom Corporation. -Copyright (c) 2015, Raspberry Pi (Trading) Ltd -All rights reserved. - -Redistribution. Redistribution and use in binary form, without -modification, are permitted provided that the following conditions are -met: - -* This software may only be used for the purposes of developing for, - running or using a Raspberry Pi device. -* Redistributions must reproduce the above copyright notice and the - following disclaimer in the documentation and/or other materials - provided with the distribution. -* Neither the name of Broadcom Corporation nor the names of its suppliers - may be used to endorse or promote products derived from this software - without specific prior written permission. - -DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. - diff --git a/lib/sdk/adapters/usbboot/blobs/raspberrypi/bootcode.bin b/lib/sdk/adapters/usbboot/blobs/raspberrypi/bootcode.bin deleted file mode 100644 index 05d3d65d..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/raspberrypi/bootcode.bin and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/raspberrypi/fixup_cd.dat b/lib/sdk/adapters/usbboot/blobs/raspberrypi/fixup_cd.dat deleted file mode 100644 index 4dd9915d..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/raspberrypi/fixup_cd.dat and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/blobs/raspberrypi/start_cd.elf b/lib/sdk/adapters/usbboot/blobs/raspberrypi/start_cd.elf deleted file mode 100644 index c224a82b..00000000 Binary files a/lib/sdk/adapters/usbboot/blobs/raspberrypi/start_cd.elf and /dev/null differ diff --git a/lib/sdk/adapters/usbboot/index.js b/lib/sdk/adapters/usbboot/index.js deleted file mode 100644 index 60052d1d..00000000 --- a/lib/sdk/adapters/usbboot/index.js +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This work is heavily based on https://github.com/raspberrypi/usbboot - * Copyright 2016 Raspberry Pi Foundation - */ - -'use strict' - -const _ = require('lodash') -const fs = require('fs') -const path = require('path') -const EventEmitter = require('events') -const Bluebird = require('bluebird') -const debug = require('debug')('etcher:sdk:usbboot') -const usb = require('./usb') -const protocol = require('./protocol') -const utils = require('../../../shared/utils') - -debug.enabled = true - -/** - * @summary The radix used by USB ID numbers - * @type {Number} - * @constant - */ -const USB_ID_RADIX = 16 - -/** - * @summary The expected length of a USB ID number - * @type {Number} - * @constant - */ -const USB_ID_LENGTH = 4 - -/** - * @summary Vendor ID of "Broadcom Corporation" - * @type {Number} - * @constant - */ -const USB_VENDOR_ID_BROADCOM_CORPORATION = 0x0a5c - -/** - * @summary Product ID of BCM2708 - * @type {Number} - * @constant - */ -const USB_PRODUCT_ID_BCM2708_BOOT = 0x2763 - -/** - * @summary Product ID of BCM2710 - * @type {Number} - * @constant - */ -const USB_PRODUCT_ID_BCM2710_BOOT = 0x2764 - -/** - * @summary The timeout for USB device operations - * @type {Number} - * @constant - */ -const USB_OPERATION_TIMEOUT_MS = 1000 - -/** - * @summary The number of USB endpoint interfaces in devices with a BCM2835 SoC - * @type {Number} - * @constant - */ -const USB_ENDPOINT_INTERFACES_SOC_BCM2835 = 1 - -/** - * @summary The USB device descriptor index of an empty property - * @type {Number} - * @constant - */ -const USB_DESCRIPTOR_NULL_INDEX = 0 - -/** - * @summary usbboot bootcode file name - * @type {String} - * @constant - */ -const USBBOOT_BOOTCODE_FILE_NAME = 'bootcode.bin' - -/** - * @summary List of usbboot capable devices - * @type {Object[]} - * @constant - */ -const USBBOOT_CAPABLE_USB_DEVICES = [ - - // BCM2835 - - { - vendorID: USB_VENDOR_ID_BROADCOM_CORPORATION, - productID: USB_PRODUCT_ID_BCM2708_BOOT - }, - - // BCM2837 - - { - vendorID: USB_VENDOR_ID_BROADCOM_CORPORATION, - productID: USB_PRODUCT_ID_BCM2710_BOOT - } - -] - -/** - * @summary Compute module descriptions - * @type {Object} - * @constant - */ -const COMPUTE_MODULE_DESCRIPTIONS = { - [USB_PRODUCT_ID_BCM2708_BOOT]: 'Compute Module 1', - [USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3' -} - -/** - * @summary Estimated device reboot delay - * @type {Number} - * @constant - */ -const DEVICE_REBOOT_DELAY = 6000 - -/** - * @summary The initial step of the file server usbboot phase - * @constant - * @type {Number} - * @private - */ -const DEFAULT_FILE_SERVER_STEP = 1 - -/** - * @summary Convert a USB id (e.g. product/vendor) to a string - * @function - * @private - * - * @param {Number} id - USB id - * @returns {String} string id - * - * @example - * console.log(usbIdToString(2652)) - * > '0x0a5c' - */ -const usbIdToString = (id) => { - return `0x${_.padStart(id.toString(USB_ID_RADIX), USB_ID_LENGTH, '0')}` -} - -/** - * @summary Check if a USB device object is usbboot-capable - * @function - * @private - * - * @param {Object} device - device - * @returns {Boolean} whether the device is usbboot-capable - * - * @example - * if (isUsbBootCapableUSBDevice({ ... })) { - * console.log('We can use usbboot on this device') - * } - */ -const isUsbBootCapableUSBDevice = (device) => { - return _.some(USBBOOT_CAPABLE_USB_DEVICES, { - vendorID: device.deviceDescriptor.idVendor, - productID: device.deviceDescriptor.idProduct - }) -} - -/** - * @summary USBBootAdapter - * @class - */ -class USBBootAdapter extends EventEmitter { - /** - * @summary USBBootAdapter constructor - * @class - * @example - * const adapter = new USBBootAdapter() - */ - constructor () { - super() - - /** @type {String} Adapter name */ - this.id = this.constructor.id - - /** @type {Object} Blob cache */ - this.blobCache = {} - - /** @type {Object} Progress hash */ - this.progress = {} - - this.devices = [] - this.on('devices', (devices) => { - this.devices = devices - }) - } - - /** - * @summary Query a blob from the internal cache - * @private - * - * @param {String} name - blob name - * @fulfil {Buffer} - blob - * @returns {Promise} - * - * @example - * const Bluebird = require('bluebird') - * const fs = Bluebird.promisifyAll(require('fs')) - * - * const blob = adapter.queryBlobFromCache('start.elf') - */ - queryBlobFromCache (name) { - if (this.blobCache[name]) { - return Bluebird.resolve(this.blobCache[name]) - } - - return USBBootAdapter.readBlob(name).tap((buffer) => { - this.blobCache[name] = buffer - }) - } - - /** - * @summary Scan for usbboot capable USB devices - * @public - * - * @description - * You should at the very least pass a file named `bootcode.bin`. - * - * @param {Object} options - options - * @param {Object} options.files - files buffers - * @param {Function} [callback] - optional callback - * @returns {USBBootAdapter} - * - * @example - * adapter.scan({ - * files: { - * 'bootcode.bin': fs.readFileSync('./msd/bootcode.bin'), - * 'start.elf': fs.readFileSync('./msd/start.elf') - * } - * }, (error, devices) => { - * // ... - * }) - */ - scan (options = {}, callback) { - /* eslint-disable lodash/prefer-lodash-method */ - usb.listDevices().filter(isUsbBootCapableUSBDevice).map((device) => { - /* eslint-enable lodash/prefer-lodash-method */ - - const description = COMPUTE_MODULE_DESCRIPTIONS[device.deviceDescriptor.idProduct] || 'Compute Module' - - if (!device.accessible) { - return { - device: `${usbIdToString(device.deviceDescriptor.idVendor)}:${usbIdToString(device.deviceDescriptor.idProduct)}`, - displayName: 'Missing drivers', - description, - mountpoints: [], - isReadOnly: false, - isSystem: false, - disabled: true, - icon: 'warning', - size: null, - link: 'https://www.raspberrypi.org/documentation/hardware/computemodule/cm-emmc-flashing.md', - linkCTA: 'Install', - linkTitle: 'Install missing drivers', - linkMessage: [ - 'Would you like to download the necessary drivers from the Raspberry Pi Foundation?', - 'This will open your browser.\n\n', - 'Once opened, download and run the installer from the "Windows Installer" section to install the drivers.' - ].join(' '), - adaptor: USBBootAdapter.id - } - } - - // This is the only way we can unique identify devices - device.raw = `${device.busNumber}:${device.deviceAddress}` - - const result = { - device: device.raw, - raw: device.raw, - displayName: 'Initializing device', - - // At this point we can't differentiate between CMs any more, so - // we can't use the description that changes depending on the PID. - description: 'Compute Module', - - size: null, - mountpoints: [], - isReadOnly: false, - isSystem: false, - disabled: true, - icon: 'loading', - vendor: usbIdToString(device.deviceDescriptor.idVendor), - product: usbIdToString(device.deviceDescriptor.idProduct), - adaptor: USBBootAdapter.id - } - - if (_.isNil(this.progress[result.raw])) { - this.prepare(device).catch((error) => { - this.emit('error', error) - }) - } - - result.progress = this.progress[result.raw] - - if (result.progress === utils.PERCENTAGE_MAXIMUM) { - return Bluebird.delay(DEVICE_REBOOT_DELAY).return(result) - } - - return result - - // See http://bluebirdjs.com/docs/api/promise.map.html - }, { - concurrency: 5 - }).catch((error) => { - this.emit('error', error) - callback && callback(error) - }).then((devices) => { - this.emit('devices', devices) - callback && callback(null, devices) - }) - - return this - } - - /** - * @summary Prepare a usbboot device - * @function - * @private - * - * @param {Object} device - node-usb device - * @returns {Promise} - * - * @example - * const fs = Bluebird.promisifyAll(require('fs')) - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * - * adapter.prepare(device, (name) => { - * return fs.readFileAsync(name) - * }).then(() => { - * console.log('Done!') - * }) - */ - prepare (device) { - /** - * @summary Set device progress - * @function - * @private - * - * @param {Number} percentage - percentage - * - * @example - * setProgress(90) - */ - const setProgress = (percentage) => { - debug(`%c[${device.raw}] -> ${Math.floor(percentage)}%%`, 'color:red;') - this.progress[device.raw] = percentage - } - - const serialNumberIndex = device.deviceDescriptor.iSerialNumber - debug(`Serial number index: ${serialNumberIndex}`) - if (serialNumberIndex === USB_DESCRIPTOR_NULL_INDEX) { - // eslint-disable-next-line no-magic-numbers - setProgress(10) - } else { - // eslint-disable-next-line no-magic-numbers - setProgress(15) - } - - return Bluebird.try(() => { - // We need to open the device in order to access _configDescriptor - debug(`Opening device: ${device.raw}`) - device.open() - - // Ensures we don't wait forever if an issue occurs - device.timeout = USB_OPERATION_TIMEOUT_MS - - // Handle 2837 where it can start with two interfaces, the first - // is mass storage the second is the vendor interface for programming - const addresses = {} - /* eslint-disable no-underscore-dangle */ - if (device._configDescriptor.bNumInterfaces === USB_ENDPOINT_INTERFACES_SOC_BCM2835) { - /* eslint-enable no-underscore-dangle */ - addresses.interface = 0 - addresses.endpoint = 1 - } else { - addresses.interface = 1 - addresses.endpoint = 3 - } - - const deviceInterface = device.interface(addresses.interface) - debug(`Claiming interface: ${addresses.interface}`) - - try { - deviceInterface.claim() - } catch (error) { - if (error.message === 'LIBUSB_ERROR_NO_DEVICE') { - debug('Couldn\'t claim the interface. Assuming the device is gone') - return null - } - - throw error - } - - const endpoint = deviceInterface.endpoint(addresses.endpoint) - - if (serialNumberIndex === USB_DESCRIPTOR_NULL_INDEX) { - return this.queryBlobFromCache(USBBOOT_BOOTCODE_FILE_NAME).then((bootcode) => { - return USBBootAdapter.writeBootCode(device, endpoint, bootcode) - }) - } - - debug('Starting file server') - - const PERCENTAGE_START = 20 - const PERCENTAGE_TOTAL = 95 - - // TODO: Find a way to not hardcode these values, and instead - // figure out the correct number for each board on the fly. - // This might be possible once we implement proper device - // auto-discovery. For now, we assume the worst case scenario. - // eslint-disable-next-line no-magic-numbers - const STEPS_TOTAL = 38 - - return this.startFileServer(device, endpoint, { - progress: (step) => { - setProgress((step * (PERCENTAGE_TOTAL - PERCENTAGE_START) / STEPS_TOTAL) + PERCENTAGE_START) - } - }).tap(() => { - setProgress(utils.PERCENTAGE_MAXIMUM) - }) - }).return(device).catch({ - message: 'LIBUSB_TRANSFER_CANCELLED' - }, { - message: 'LIBUSB_ERROR_NO_DEVICE' - }, _.constant(null)).tap((result) => { - if (result) { - result.close() - } - }).finally(() => { - return Bluebird.delay(DEVICE_REBOOT_DELAY).then(() => { - Reflect.deleteProperty(this.progress, device.raw) - }) - }) - } - - /** - * @summary Write bootcode to USB device (usbboot first stage) - * @private - * - * @description - * After this stage is run, the USB will be re-mounted as 0x0a5c:0x2764. - * - * @param {Object} device - node-usb device - * @param {Object} endpoint - node-usb endpoint - * @param {Buffer} bootCodeBuffer - bootcode buffer - * @returns {Promise} - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * const bootcode = fs.readFileSync('./bootcode.bin') - * - * adapter.writeBootCode(device, device.interfaces(0).endpoint(1), bootcode).then(() => { - * console.log('Done!') - * }) - */ - static writeBootCode (device, endpoint, bootCodeBuffer) { - debug('Writing bootcode') - debug(`Bootcode buffer length: ${bootCodeBuffer.length}`) - const bootMessageBuffer = protocol.createBootMessageBuffer(bootCodeBuffer.length) - - debug('Writing boot message buffer to out endpoint') - return protocol.write(device, endpoint, bootMessageBuffer).then(() => { - debug('Writing boot code buffer to out endpoint') - return protocol.write(device, endpoint, bootCodeBuffer) - }).then(() => { - debug('Reading return code from device') - return protocol.read(device, protocol.RETURN_CODE_LENGTH) - }).then((data) => { - const returnCode = data.readInt32LE() - debug(`Received return code: ${returnCode}`) - - if (returnCode !== protocol.RETURN_CODE_SUCCESS) { - throw new Error(`Couldn't write the bootcode, got return code ${returnCode} from device`) - } - }) - } - - /** - * @summary Mount a USB device as a block device (usbboot second stage) - * @private - * - * @description - * The possible files you can pass here are: - * - * - autoboot.txt - * - config.txt - * - recovery.elf - * - start.elf - * - fixup.dat - * - * @param {Object} device - node-usb device - * @param {Object} endpoint - node-usb endpoint - * @param {Object} options - options - * @param {Function} options.progress - progress function (step) - * @param {Number} [step] - current step (used internally) - * @returns {Promise} - * - * @example - * const fs = Bluebird.promisifyAll(require('fs')) - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * - * adapter.startFileServer(device, device.interfaces(0).endpoint(1), { - * progress: (step) => { - * console.log(`Currently on step ${step}`) - * } - * }).then(() => { - * console.log('Done!') - * }) - */ - startFileServer (device, endpoint, options, step = DEFAULT_FILE_SERVER_STEP) { - debug(`Listening for file messages (step ${step})`) - options.progress(step) - return protocol - .read(device, protocol.FILE_MESSAGE_SIZE) - .then(protocol.parseFileMessageBuffer) - - // We get these error messages when reading a command - // from the device when the communication has ended - .catch({ - message: 'LIBUSB_TRANSFER_STALL' - }, { - message: 'LIBUSB_TRANSFER_ERROR' - }, (error) => { - debug(`Got ${error.message} when reading a command, assuming everything is done`) - return { - command: protocol.FILE_MESSAGE_COMMANDS.DONE - } - }) - - .then((fileMessage) => { - debug(`Received message: ${fileMessage.command} -> ${fileMessage.fileName}`) - - if (fileMessage.command === protocol.FILE_MESSAGE_COMMANDS.DONE) { - debug('Done') - return Bluebird.resolve() - } - - return Bluebird.try(() => { - if (fileMessage.command === protocol.FILE_MESSAGE_COMMANDS.GET_FILE_SIZE) { - debug(`Getting the size of ${fileMessage.fileName}`) - - return this.queryBlobFromCache(fileMessage.fileName).then((fileBuffer) => { - const fileSize = fileBuffer.length - debug(`Sending size: ${fileSize}`) - return protocol.sendBufferSize(device, fileSize) - }).catch({ - code: 'ENOENT' - }, () => { - debug(`Couldn't find ${fileMessage.fileName}`) - debug('Sending error signal') - return protocol.sendErrorSignal(device) - }) - } - - if (fileMessage.command === protocol.FILE_MESSAGE_COMMANDS.READ_FILE) { - debug(`Reading ${fileMessage.fileName}`) - - return this.queryBlobFromCache(fileMessage.fileName).then((fileBuffer) => { - return protocol.write(device, endpoint, fileBuffer) - }).catch({ - code: 'ENOENT' - }, () => { - debug(`Couldn't find ${fileMessage.fileName}`) - debug('Sending error signal') - return protocol.sendErrorSignal(device) - }) - } - - return Bluebird.reject(new Error(`Unrecognized command: ${fileMessage.command}`)) - }).then(() => { - debug('Starting again') - const STEP_INCREMENT = 1 - return this.startFileServer(device, endpoint, options, step + STEP_INCREMENT) - }) - }) - } -} - -/** - * @summary The name of this adapter - * @public - * @type {String} - * @constant - */ -USBBootAdapter.id = 'usbboot' - -/** - * @summary Read a usbboot blob - * @private - * - * @param {String} filename - blob name - * @fulfil {Buffer} - blob - * @returns {Promise} - * - * @example - * USBBootAdapter.readBlob('bootcode.bin') - * .then((buffer) => { ... }) - * .catch((error) => { ... }) - */ -USBBootAdapter.readBlob = (filename) => { - const isRaspberryPi = _.includes([ - 'bootcode.bin', - 'start_cd.elf', - 'fixup_cd.dat' - ], filename) - - const blobPath = isRaspberryPi - ? path.join('raspberrypi', filename) - : filename - - return fs.readFileAsync(path.join(__dirname, 'blobs', blobPath)) -} - -// Exports -module.exports = USBBootAdapter diff --git a/lib/sdk/adapters/usbboot/protocol.js b/lib/sdk/adapters/usbboot/protocol.js deleted file mode 100644 index 12b8fdaa..00000000 --- a/lib/sdk/adapters/usbboot/protocol.js +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * This work is heavily based on https://github.com/raspberrypi/usbboot - * Copyright 2016 Raspberry Pi Foundation - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const usb = require('./usb') - -// The equivalent of a NULL buffer, given that node-usb complains -// if the data argument is not an instance of Buffer -const NULL_BUFFER_SIZE = 0 -const NULL_BUFFER = Buffer.alloc(NULL_BUFFER_SIZE) - -const ONE_MEGABYTE = 1048576 - -/** - * @summary The size of the boot message bootcode length section - * @type {Number} - * @constant - */ -const BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE = 4 - -/** - * @summary The offset of the boot message bootcode length section - * @type {Number} - * @constant - */ -const BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET = 0 - -/** - * @summary The size of the boot message signature section - * @type {Number} - * @constant - */ -const BOOT_MESSAGE_SIGNATURE_SIZE = 20 - -/** - * @summary The offset of the file message command section - * @type {Number} - * @constant - */ -const FILE_MESSAGE_COMMAND_OFFSET = 0 - -/** - * @summary The size of the file message command section - * @type {Number} - * @constant - */ -const FILE_MESSAGE_COMMAND_SIZE = 4 - -/** - * @summary The offset of the file message file name section - * @type {Number} - * @constant - */ -const FILE_MESSAGE_FILE_NAME_OFFSET = FILE_MESSAGE_COMMAND_SIZE - -/** - * @summary The size of the file message file name section - * @type {Number} - * @constant - */ -const FILE_MESSAGE_FILE_NAME_SIZE = 256 - -/** - * @summary The GET_STATUS usb control transfer request code - * @type {Number} - * @constant - * @description - * See http://www.jungo.com/st/support/documentation/windriver/811/wdusb_man_mhtml/node55.html#usb_standard_dev_req_codes - */ -const USB_REQUEST_CODE_GET_STATUS = 0 - -/** - * @summary The maximum buffer length of a usbboot message - * @type {Number} - * @constant - */ -const USBBOOT_MESSAGE_MAX_BUFFER_LENGTH = 0xffff - -/** - * @summary The delay to wait between each USB read/write operation - * @type {Number} - * @constant - * @description - * The USB bus seems to hang if we execute many operations at - * the same time. - */ -const USB_REQUEST_DELAY_MS = 1000 - -/** - * @summary The timeout for USB bulk transfers, in milliseconds - * @type {Number} - * @constant - */ -// In node-usb, 0 means "infinite" timeout -const USB_BULK_TRANSFER_TIMEOUT_MS = 0 - -/** - * @summary The amount of bits to shift to the right on a control transfer index - * @type {Number} - * @constant - */ -const CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT = 16 - -/** - * @summary The size of the usbboot file message - * @type {Number} - * @constant - */ -exports.FILE_MESSAGE_SIZE = FILE_MESSAGE_COMMAND_SIZE + FILE_MESSAGE_FILE_NAME_SIZE - -/** - * @summary File message command display names - * @namespace FILE_MESSAGE_COMMANDS - * @public - */ -exports.FILE_MESSAGE_COMMANDS = { - - /** - * @property {String} - * @memberof FILE_MESSAGE_COMMANDS - * - * @description - * The "get file size" file message command name. - */ - GET_FILE_SIZE: 'GetFileSize', - - /** - * @property {String} - * @memberof FILE_MESSAGE_COMMANDS - * - * @description - * The "read file" file message command name. - */ - READ_FILE: 'ReadFile', - - /** - * @property {String} - * @memberof FILE_MESSAGE_COMMANDS - * - * @description - * The "done" file message command name. - */ - DONE: 'Done' -} - -/** - * @summary The usbboot return code that represents success - * @type {Number} - * @constant - */ -exports.RETURN_CODE_SUCCESS = 0 - -/** - * @summary The buffer length of the return code message - * @type {Number} - * @constant - */ -exports.RETURN_CODE_LENGTH = 4 - -/** - * @summary Send a buffer size to a device as a control transfer - * @function - * @public - * - * @param {Object} device - node-usb device - * @param {Number} size - buffer size - * @returns {Promise} - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * - * protocol.sendBufferSize(device, 512).then(() => { - * console.log('Done!') - * }) - */ -exports.sendBufferSize = (device, size) => { - return usb.performControlTransfer(device, { - bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR, - bRequest: USB_REQUEST_CODE_GET_STATUS, - data: NULL_BUFFER, - - /* eslint-disable no-bitwise */ - wValue: size & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH, - wIndex: size >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT - /* eslint-enable no-bitwise */ - }) -} - -const chunks = function *(buffer, size) { - for (let start = 0; start < buffer.length; start += size) { - yield buffer.slice(start, start + size) - } -} - -/** - * @summary Write a buffer to an OUT endpoint - * @function - * @private - * - * @param {Object} device - device - * @param {Object} endpoint - endpoint - * @param {Buffer} buffer - buffer - * @returns {Promise} - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * return protocol.write(device, device.interface(0).endpoint(1), Buffer.alloc(1)).then(() => { - * console.log('Done!') - * }) - */ -exports.write = (device, endpoint, buffer) => { - return exports.sendBufferSize(device, buffer.length) - - // We get LIBUSB_TRANSFER_STALL sometimes - // in future bulk transfers without this - .delay(USB_REQUEST_DELAY_MS) - - .then(() => { - endpoint.timeout = USB_BULK_TRANSFER_TIMEOUT_MS - return Bluebird.each(chunks(buffer, ONE_MEGABYTE), (chunk) => { - return Bluebird.fromCallback((callback) => { - endpoint.transfer(chunk, callback) - }) - }) - }) -} - -/** - * @summary Send an error signal to a device - * @function - * @public - * - * @param {Object} device - node-usb device - * @returns {Promise} - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * - * protocol.sendErrorSignal(device).then(() => { - * console.log('Done!') - * }) - */ -exports.sendErrorSignal = (device) => { - return exports.sendBufferSize(device, NULL_BUFFER_SIZE) -} - -/** - * @summary Read a buffer from a device - * @function - * @private - * - * @param {Object} device - device - * @param {Number} bytesToRead - bytes to read - * @fulfil {Buffer} - data - * @returns {Promise} - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * protocol.read(device, 4).then((data) => { - * console.log(data.readInt32BE()) - * }) - */ -exports.read = (device, bytesToRead) => { - return usb.performControlTransfer(device, { - /* eslint-disable no-bitwise */ - bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR | usb.LIBUSB_ENDPOINT_IN, - wValue: bytesToRead & USBBOOT_MESSAGE_MAX_BUFFER_LENGTH, - wIndex: bytesToRead >> CONTROL_TRANSFER_INDEX_RIGHT_BIT_SHIFT, - /* eslint-enable no-bitwise */ - - bRequest: USB_REQUEST_CODE_GET_STATUS, - length: bytesToRead - }) -} - -/** - * @summary Create a boot message buffer - * @function - * @private - * - * @description - * This is based on the following data structure: - * - * typedef struct MESSAGE_S { - * int length; - * unsigned char signature[20]; - * } boot_message_t; - * - * This needs to be sent to the out endpoint of the USB device - * as a 24 bytes big-endian buffer where: - * - * - The first 4 bytes contain the size of the bootcode.bin buffer - * - The remaining 20 bytes contain the boot signature, which - * we don't make use of in this implementation - * - * @param {Number} bootCodeBufferLength - bootcode.bin buffer length - * @returns {Buffer} boot message buffer - * - * @example - * const bootMessageBuffer = protocol.createBootMessageBuffer(50216) - */ -exports.createBootMessageBuffer = (bootCodeBufferLength) => { - const bootMessageBufferSize = BOOT_MESSAGE_BOOTCODE_LENGTH_SIZE + BOOT_MESSAGE_SIGNATURE_SIZE - - // Buffers are automatically filled with zero bytes - const bootMessageBuffer = Buffer.alloc(bootMessageBufferSize) - - // The bootcode length should be stored in 4 big-endian bytes - bootMessageBuffer.writeInt32BE(bootCodeBufferLength, BOOT_MESSAGE_BOOTCODE_LENGTH_OFFSET) - - return bootMessageBuffer -} - -/** - * @summary Parse a file message buffer from a device - * @function - * @public - * - * @param {Buffer} fileMessageBuffer - file message buffer - * @returns {Object} parsed file message - * - * @example - * const usb = require('usb') - * const device = usb.findByIds(0x0a5c, 0x2763) - * - * return protocol.read(device, protocol.FILE_MESSAGE_SIZE).then((fileMessageBuffer) => { - * return protocol.parseFileMessageBuffer(fileMessageBuffer) - * }).then((fileMessage) => { - * console.log(fileMessage.command) - * console.log(fileMessage.fileName) - * }) - */ -exports.parseFileMessageBuffer = (fileMessageBuffer) => { - const commandCode = fileMessageBuffer.readInt32LE(FILE_MESSAGE_COMMAND_OFFSET) - const command = _.nth([ - exports.FILE_MESSAGE_COMMANDS.GET_FILE_SIZE, - exports.FILE_MESSAGE_COMMANDS.READ_FILE, - exports.FILE_MESSAGE_COMMANDS.DONE - ], commandCode) - - if (_.isNil(command)) { - throw new Error(`Invalid file message command code: ${commandCode}`) - } - - const fileName = _.chain(fileMessageBuffer.toString('ascii', FILE_MESSAGE_FILE_NAME_OFFSET)) - - // The parsed string will likely contain tons of trailing - // null bytes that we should get rid of for convenience - // See https://github.com/nodejs/node/issues/4775 - .takeWhile((character) => { - return character !== '\0' - }) - .join('') - .value() - - // A blank file name can also mean "done" - if (_.isEmpty(fileName)) { - return { - command: exports.FILE_MESSAGE_COMMANDS.DONE - } - } - - return { - command, - fileName - } -} diff --git a/lib/sdk/adapters/usbboot/usb.js b/lib/sdk/adapters/usbboot/usb.js deleted file mode 100644 index 7cf78930..00000000 --- a/lib/sdk/adapters/usbboot/usb.js +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const debug = require('debug')('etcher:sdk:usbboot') - -// The USB module calls `libusb_init`, which will fail -// if the device we're running in has no USB controller -// plugged in (e.g. in certain CI services). -// In order to workaround that, we need to return a -// stub if such error occurs. -const usb = (() => { - try { - return require('usb') - } catch (error) { - debug('Couldn\'t require "usb". Reason: ', error.message, error.stack) - return { - getDeviceList: _.constant([]) - } - } -})() - -// Re-expose some `usb` constants -_.each([ - 'LIBUSB_REQUEST_TYPE_VENDOR', - 'LIBUSB_ENDPOINT_IN', - 'LIBUSB_TRANSFER_TYPE_BULK', - 'LIBUSB_ERROR_NO_DEVICE', - 'LIBUSB_ERROR_IO' -], (constant) => { - exports[constant] = usb[constant] -}) - -/** - * @summary The timeout for USB control transfers, in milliseconds - * @type {Number} - * @constant - */ -// In node-usb, 0 means "infinite" timeout -const USB_CONTROL_TRANSFER_TIMEOUT_MS = 0 - -/** - * @summary List the available USB devices - * @function - * @public - * - * @fulfil {Object[]} - usb devices - * @returns {Promise} - * - * @example - * usb.listDevices().each((device) => { - * console.log(device) - * }) - */ -exports.listDevices = () => { - const devices = _.map(usb.getDeviceList(), (device) => { - device.accessible = true - return device - }) - - // Include driverless devices into the list of USB devices. - if (process.platform === 'win32') { - // NOTE: Temporarily ignore errors when loading winusb-driver-generator, - // due to C Runtime issues on Windows; - // see https://github.com/resin-io/etcher/issues/1956 - try { - /* eslint-disable node/no-missing-require */ - const winusbDriverGenerator = require('winusb-driver-generator') - /* eslint-enable node/no-missing-require */ - for (const device of winusbDriverGenerator.listDriverlessDevices()) { - devices.push({ - accessible: false, - deviceDescriptor: { - idVendor: device.vid, - idProduct: device.pid - } - }) - } - } catch (error) { - // Ignore error - } - } - - return Bluebird.resolve(devices) -} - -/** - * @summary Get a USB device string from an index - * @function - * @public - * - * @param {Object} device - device - * @param {Number} index - string index - * @fulfil {String} - string - * @returns {Promise} - * - * @example - * usb.getDeviceStringFromIndex({ ... }, 5).then((string) => { - * console.log(string) - * }) - */ -exports.getDeviceStringFromIndex = (device, index) => { - return Bluebird.fromCallback((callback) => { - device.getStringDescriptor(index, callback) - }) -} - -/** - * @summary Perform a USB control transfer - * @function - * @public - * - * @description - * See http://libusb.sourceforge.net/api-1.0/group__syncio.html - * - * @param {Object} device - usb device - * @param {Object} options - options - * @param {Number} options.bmRequestType - the request type field for the setup packet - * @param {Number} options.bRequest - the request field for the setup packet - * @param {Number} options.wValue - the value field for the setup packet - * @param {Number} options.wIndex - the index field for the setup packet - * @param {Buffer} [options.data] - output data buffer (for OUT transfers) - * @param {Number} [options.length] - input data size (for IN transfers) - * @fulfil {(Buffer|Undefined)} - result - * @returns {Promise} - * - * @example - * const buffer = Buffer.alloc(512) - * - * usb.performControlTransfer({ ... }, { - * bmRequestType: usb.LIBUSB_REQUEST_TYPE_VENDOR - * bRequest: 0, - * wValue: buffer.length & 0xffff, - * wIndex: buffer.length >> 16, - * data: Buffer.alloc(256) - * }) - */ -exports.performControlTransfer = (device, options) => { - if (_.isNil(options.data) && _.isNil(options.length)) { - return Bluebird.reject(new Error('You must define either data or length')) - } - - if (!_.isNil(options.data) && !_.isNil(options.length)) { - return Bluebird.reject(new Error('You can define either data or length, but not both')) - } - - return Bluebird.fromCallback((callback) => { - device.timeout = USB_CONTROL_TRANSFER_TIMEOUT_MS - device.controlTransfer( - options.bmRequestType, - options.bRequest, - options.wValue, - options.wIndex, - options.data || options.length, - callback - ) - }) -} diff --git a/lib/sdk/image-stream/README.md b/lib/sdk/image-stream/README.md deleted file mode 100644 index da87115f..00000000 --- a/lib/sdk/image-stream/README.md +++ /dev/null @@ -1,76 +0,0 @@ -Etcher Image Stream -=================== - -This module is in charge of creating a readable stream from any image source -(e.g: a file, a URL, etc) along with some metadata (like size), and handling -any necessary transformations (like decompression) that must be applied before -plugging the stream to [`etcher-image-write`][etcher-image-write]. - -Given that this module contains the logic to handle image formats, the module -becomes the most reliable source of truth for the list of supported ones. - -There are three classes of images this module supports: - -- Uncompressed images (e.g: `.img`, `.iso`) -- Compressed images (e.g: `.img.xz`, `.iso.gz`) -- Archive images (e.g: `.zip`) - -The core of this module consists of handlers and archive hooks. - -Handlers --------- - -The handlers are functions that know how to handle certain MIME types, like -`application/x-bzip2` and `application/octet-stream`, returning a stream for -the image, a transform stream that needs to be applied to get the real image -data, and useful metadata like the final image size. - -Each handler is called with a file path (although that will change soon once we -add proper support for URLs) and an options object, containing extra metadata -about the file. - -Archive Hooks -------------- - -This module supports reading "archive images", which are defined by handlers -(like `application/zip`). In order to avoid duplication on how to handle -archives, archive support is implemented by "archive hooks". - -Archive hooks are CommonJS modules that expose two functions: - -- `Promise .getEntries(String archivePath)`: list all entries in the archive -- `Stream.Readable .extractFile(String archivePath, String[] entries, String entry)`: get a readable stream for an archive entry - -Defining those two functions for any archive format is enough for Etcher to -correctly use its archive handling logic on them. - -Archive Images --------------- - -As mentioned before, Etcher supports the concept of "archive images". These are -uncompressed image files included *inside* an archive format, like `.zip` or -`.tar`, possibly along other files. - -These are the rules for handling archive images: - -- Each archive should only contain one valid image -- Images in archives should be in uncompressed form - -The module throws an error if the above rules are not met. - -Supported Formats ------------------ - -There are currently three image types in supported formats: `image`, `compressed` and `archive`. - -An extension tagged `image` describes a format which can be directly written to a device by its handler, -and an extension tagged `archive` denotes an archive containing an image, and will cause an archive handler -to open the archive and search for an image file. - -Note that when marking an extension as `compressed`, the filename will be stripped of that extension, -and the leftover extension examined to determine the uncompressed image format (i.e. `.img.gz -> .img`). - -As an archive (such as `.tar`) might be additionally compressed, this will allow for constructs such as -`.tar.gz` (a compressed archive, containing a file with an extension tagged as `image`) to be handled correctly. - -[etcher-image-write]: https://github.com/balena-io-modules/etcher-image-write diff --git a/lib/sdk/image-stream/archive-hooks/zip.js b/lib/sdk/image-stream/archive-hooks/zip.js deleted file mode 100644 index 79879e7c..00000000 --- a/lib/sdk/image-stream/archive-hooks/zip.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const Bluebird = require('bluebird') -const _ = require('lodash') -const StreamZip = require('node-stream-zip') -const yauzl = Bluebird.promisifyAll(require('yauzl')) -const errors = require('../../../shared/errors') - -/** - * @summary Get all archive entries - * @function - * @public - * - * @param {String} archive - archive path - * @fulfil {Object[]} - archive entries - * @returns {Promise} - * - * @example - * zip.getEntries('path/to/my.zip').then((entries) => { - * entries.forEach((entry) => { - * console.log(entry.name); - * console.log(entry.size); - * }); - * }); - */ -exports.getEntries = (archive) => { - return new Bluebird((resolve, reject) => { - const zip = new StreamZip({ - file: archive, - storeEntries: true - }) - - zip.on('error', reject) - - zip.on('ready', () => { - const EMPTY_ENTRY_SIZE = 0 - - return resolve(_.chain(zip.entries()) - .omitBy({ - size: EMPTY_ENTRY_SIZE - }) - .map((metadata) => { - return { - name: metadata.name, - size: metadata.size - } - }) - .value()) - }) - }) -} - -/** - * @summary Extract a file from an archive - * @function - * @public - * - * @param {String} archive - archive path - * @param {String[]} entries - archive entries - * @param {String} file - archive file - * @fulfil {ReadableStream} file - * @returns {Promise} - * - * @example - * zip.getEntries('path/to/my.zip').then((entries) => { - * return zip.extractFile('path/to/my.zip', entries, 'my/file'); - * }).then((stream) => { - * stream.pipe('...'); - * }); - */ -exports.extractFile = (archive, entries, file) => { - return new Bluebird((resolve, reject) => { - if (!_.find(entries, { - name: file - })) { - throw errors.createError({ - title: `Invalid entry: ${file}` - }) - } - - yauzl.openAsync(archive, { - lazyEntries: true - }).then((zipfile) => { - zipfile.readEntry() - - zipfile.on('entry', (entry) => { - if (entry.fileName !== file) { - return zipfile.readEntry() - } - - return zipfile.openReadStream(entry, (error, readStream) => { - if (error) { - return reject(error) - } - - return resolve(readStream) - }) - }) - }).catch(reject) - }) -} diff --git a/lib/sdk/image-stream/archive.js b/lib/sdk/image-stream/archive.js deleted file mode 100644 index 38f79a11..00000000 --- a/lib/sdk/image-stream/archive.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const Bluebird = require('bluebird') -const _ = require('lodash') -const PassThroughStream = require('stream').PassThrough -const supportedFileTypes = require('./supported') -const utils = require('./utils') -const errors = require('../../shared/errors') -const fileExtensions = require('../../shared/file-extensions') - -/** - * @summary Archive metadata base path - * @constant - * @private - * @type {String} - */ -const ARCHIVE_METADATA_BASE_PATH = '.meta' - -/** - * @summary Image extensions - * @constant - * @private - * @type {String[]} - */ -const IMAGE_EXTENSIONS = _.reduce(supportedFileTypes, (accumulator, file) => { - if (file.type === 'image') { - accumulator.push(file.extension) - } - - return accumulator -}, []) - -/** - * @summary Extract entry by path - * @function - * @private - * - * @param {String} archive - archive - * @param {String} filePath - entry file path - * @param {Object} options - options - * @param {Object} options.hooks - archive hooks - * @param {Object[]} options.entries - archive entries - * @param {*} [options.default] - entry default value - * @fulfil {*} contents - * @returns {Promise} - * - * @example - * extractEntryByPath('my/archive.zip', '_info/logo.svg', { - * hooks: { ... }, - * entries: [ ... ], - * default: '' - * }).then((contents) => { - * console.log(contents); - * }); - */ -const extractEntryByPath = (archive, filePath, options) => { - const fileEntry = _.find(options.entries, (entry) => { - return _.chain(entry.name) - .split('/') - .tail() - .join('/') - .value() === filePath - }) - - if (!fileEntry) { - return Bluebird.resolve(options.default) - } - - return options.hooks.extractFile(archive, options.entries, fileEntry.name) - .then(utils.extractStream) -} - -/** - * @summary Extract archive metadata - * @function - * @private - * - * @param {String} archive - archive - * @param {String} basePath - metadata base path - * @param {Object} options - options - * @param {Object[]} options.entries - archive entries - * @param {Object} options.hooks - archive hooks - * @fulfil {Object} - metadata - * @returns {Promise} - * - * @example - * extractArchiveMetadata('my/archive.zip', '.meta', { - * hooks: { ... }, - * entries: [ ... ] - * }).then((metadata) => { - * console.log(metadata); - * }); - */ -const extractArchiveMetadata = (archive, basePath, options) => { - return Bluebird.props({ - logo: extractEntryByPath(archive, `${basePath}/logo.svg`, options), - instructions: extractEntryByPath(archive, `${basePath}/instructions.markdown`, options), - bmap: extractEntryByPath(archive, `${basePath}/image.bmap`, options), - manifest: _.attempt(() => { - return extractEntryByPath(archive, `${basePath}/manifest.json`, { - hooks: options.hooks, - entries: options.entries, - default: '{}' - }).then((manifest) => { - try { - return JSON.parse(manifest) - } catch (parseError) { - throw errors.createUserError({ - title: 'Invalid archive manifest.json', - description: 'The archive manifest.json file is not valid JSON' - }) - } - }) - }) - }).then((results) => { - return { - name: results.manifest.name, - version: results.manifest.version, - url: results.manifest.url, - supportUrl: results.manifest.supportUrl, - releaseNotesUrl: results.manifest.releaseNotesUrl, - checksumType: results.manifest.checksumType, - checksum: results.manifest.checksum, - bytesToZeroOutFromTheBeginning: results.manifest.bytesToZeroOutFromTheBeginning, - recommendedDriveSize: results.manifest.recommendedDriveSize, - logo: _.invoke(results.logo, [ 'toString' ]), - bmap: _.invoke(results.bmap, [ 'toString' ]), - instructions: _.invoke(results.instructions, [ 'toString' ]) - } - }) -} - -/** - * @summary Extract image from archive - * @function - * @public - * - * @param {String} archive - archive path - * @param {Object} hooks - archive hooks - * @param {Function} hooks.getEntries - get entries hook - * @param {Function} hooks.extractFile - extract file hook - * @fulfil {Object} image metadata - * @returns {Promise} - * - * @example - * archive.extractImage('path/to/my/archive.zip', { - * getEntries: (archive) => { - * return [ ..., ..., ... ]; - * }, - * extractFile: (archive, entries, file) => { - * ... - * } - * }).then((image) => { - * image.stream.pipe(image.transform).pipe(...); - * }); - */ -exports.extractImage = (archive, hooks) => { - return hooks.getEntries(archive).then((entries) => { - const imageEntries = _.filter(entries, (entry) => { - return _.includes(IMAGE_EXTENSIONS, fileExtensions.getLastFileExtension(entry.name)) - }) - - const VALID_NUMBER_OF_IMAGE_ENTRIES = 1 - if (imageEntries.length !== VALID_NUMBER_OF_IMAGE_ENTRIES) { - throw errors.createUserError({ - title: 'Invalid archive image', - description: 'The archive image should contain one and only one top image file' - }) - } - - const imageEntry = _.first(imageEntries) - - return Bluebird.props({ - imageStream: hooks.extractFile(archive, entries, imageEntry.name), - metadata: extractArchiveMetadata(archive, ARCHIVE_METADATA_BASE_PATH, { - entries, - hooks - }) - }).then((results) => { - results.metadata.stream = results.imageStream - results.metadata.transform = new PassThroughStream() - results.metadata.path = archive - - results.metadata.size = { - original: imageEntry.size, - final: { - estimation: false, - value: imageEntry.size - } - } - - results.metadata.extension = fileExtensions.getLastFileExtension(imageEntry.name) - results.metadata.archiveExtension = fileExtensions.getLastFileExtension(archive) - - return results.metadata - }) - }) -} diff --git a/lib/sdk/image-stream/gzip.js b/lib/sdk/image-stream/gzip.js deleted file mode 100644 index 0d1de349..00000000 --- a/lib/sdk/image-stream/gzip.js +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -/** - * @summary The byte length of ISIZE - * @type {Number} - * @constant - * @description - * See https://tools.ietf.org/html/rfc1952 - */ -const ISIZE_LENGTH = 4 - -/** - * @summary Get the estimated uncompressed size of a gzip file - * @function - * @public - * - * @description - * This function determines the uncompressed size of the gzip file - * by reading its `ISIZE` field at the end of the file. The specification - * clarifies that this value is just an estimation. - * - * @param {Object} options - options - * @param {Number} options.size - file size - * @param {Function} options.read - read function (position, count) - * @fulfil {Number} - uncompressed size - * @returns {Promise} - * - * @example - * const fd = fs.openSync('path/to/image', 'r'); - * - * gzip.getUncompressedSize({ - * size: fs.statSync('path/to/image.gz').size, - * read: (position, count) => { - * const buffer = Buffer.alloc(count); - * return new Promise((resolve, reject) => { - * fs.read(fd, buffer, 0, count, position, (error) => { - * if (error) { - * return reject(error); - * } - * - * resolve(buffer); - * }); - * }); - * } - * }).then((uncompressedSize) => { - * console.log(`The uncompressed size is: ${uncompressedSize}`); - * fs.closeSync(fd); - * }); - */ -exports.getUncompressedSize = (options) => { - const ISIZE_BUFFER_START = 0 - const ISIZE_POSITION = options.size - ISIZE_LENGTH - return options.read(ISIZE_POSITION, ISIZE_LENGTH).then((buffer) => { - return buffer.readUInt32LE(ISIZE_BUFFER_START) - }) -} diff --git a/lib/sdk/image-stream/handlers.js b/lib/sdk/image-stream/handlers.js deleted file mode 100644 index 75f2d690..00000000 --- a/lib/sdk/image-stream/handlers.js +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -/* eslint-disable jsdoc/require-example */ - -const Bluebird = require('bluebird') -const fs = Bluebird.promisifyAll(require('fs')) -const PassThroughStream = require('stream').PassThrough -const lzma = Bluebird.promisifyAll(require('lzma-native')) -const zlib = require('zlib') -const unbzip2Stream = require('unbzip2-stream') -const gzip = require('./gzip') -const udif = Bluebird.promisifyAll(require('udif')) -const archive = require('./archive') -const utils = require('./utils') -const zipArchiveHooks = require('./archive-hooks/zip') -const fileExtensions = require('../../shared/file-extensions') -const path = require('path') -const errors = require('../../shared/errors') - -/** - * @summary Default image extension to be assumed - * @type {String} - * @constant - */ -const DEFAULT_EXT = 'img' - -/** - * @summary Default read-stream highWaterMark value (1M) - * @type {Number} - * @constant - */ -const STREAM_HWM = 1048576 - -/** - * @summary Image handlers - * @namespace handlers - * @public - */ -module.exports = { - - /** - * @summary Handle BZ2 compressed images - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @param {Object} options - options - * @param {Number} [options.size] - image size - * - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/x-bzip2': (imagePath, options) => { - return { - path: imagePath, - archiveExtension: fileExtensions.getLastFileExtension(imagePath), - extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT, - stream: fs.createReadStream(imagePath, { highWaterMark: STREAM_HWM }), - size: { - original: options.size, - final: { - estimation: true, - value: options.size - } - }, - transform: unbzip2Stream() - } - }, - - /** - * @summary Handle GZ compressed images - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @param {Object} options - options - * @param {Number} [options.size] - image size - * - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/gzip': (imagePath, options) => { - return Bluebird.using(fs.openAsync(imagePath, 'r').disposer((fileDescriptor) => { - return fs.closeAsync(fileDescriptor) - }), (fileDescriptor) => { - return gzip.getUncompressedSize({ - size: options.size, - read: (position, count) => { - return utils.readBufferFromImageFileDescriptor(fileDescriptor, position, count) - } - }) - }).then((uncompressedSize) => { - return { - path: imagePath, - archiveExtension: fileExtensions.getLastFileExtension(imagePath), - extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT, - stream: fs.createReadStream(imagePath, { highWaterMark: STREAM_HWM }), - size: { - original: options.size, - final: { - estimation: true, - value: uncompressedSize - } - }, - transform: zlib.createGunzip() - } - }) - }, - - /** - * @summary Handle XZ compressed images - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @param {Object} options - options - * @param {Number} [options.size] - image size - * - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/x-xz': (imagePath, options) => { - return Bluebird.using(fs.openAsync(imagePath, 'r').disposer((fileDescriptor) => { - return fs.closeAsync(fileDescriptor) - }), (fileDescriptor) => { - return lzma.parseFileIndexAsync({ - fileSize: options.size, - read: (count, position, callback) => { - utils.readBufferFromImageFileDescriptor(fileDescriptor, position, count).asCallback(callback) - } - }) - }).then((metadata) => { - return { - path: imagePath, - archiveExtension: fileExtensions.getLastFileExtension(imagePath), - extension: fileExtensions.getPenultimateFileExtension(imagePath) || DEFAULT_EXT, - stream: fs.createReadStream(imagePath, { highWaterMark: STREAM_HWM }), - size: { - original: options.size, - final: { - estimation: false, - value: metadata.uncompressedSize - } - }, - transform: lzma.createDecompressor() - } - }) - }, - - /** - * @summary Handle Apple disk images (.dmg) - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @param {Object} options - options - * @param {Number} [options.size] - image size - * - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/x-apple-diskimage': (imagePath, options) => { - return udif.getUncompressedSizeAsync(imagePath).then((size) => { - return { - path: imagePath, - extension: fileExtensions.getLastFileExtension(imagePath), - stream: udif.createReadStream(imagePath, { highWaterMark: STREAM_HWM }), - size: { - original: options.size, - final: { - estimation: false, - value: size - } - }, - transform: new PassThroughStream() - } - }).catch((error) => { - if (/invalid footer/i.test(error.message)) { - throw errors.createUserError({ - title: 'Invalid image', - description: `There was an error reading "${path.basename(imagePath)}". ` + - 'The image does not appear to be a valid Apple Disk Image (dmg), or may have the wrong filename extension.\n\n' + - `Error: ${error.description || error.message}` - }) - } - throw error - }) - }, - - /** - * @summary Handle ZIP compressed images - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/zip': (imagePath) => { - return archive.extractImage(imagePath, zipArchiveHooks) - }, - - /** - * @summary Handle plain uncompressed images - * @function - * @public - * @memberof handlers - * - * @param {String} imagePath - image path - * @param {Object} options - options - * @param {Number} [options.size] - image size - * - * @fulfil {Object} - image metadata - * @returns {Promise} - */ - 'application/octet-stream': (imagePath, options) => { - return { - path: imagePath, - extension: fileExtensions.getLastFileExtension(imagePath), - stream: fs.createReadStream(imagePath, { highWaterMark: STREAM_HWM }), - size: { - original: options.size, - final: { - estimation: false, - value: options.size - } - }, - transform: new PassThroughStream() - } - } - -} diff --git a/lib/sdk/image-stream/index.js b/lib/sdk/image-stream/index.js deleted file mode 100644 index c3c29e9f..00000000 --- a/lib/sdk/image-stream/index.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const fs = Bluebird.promisifyAll(require('fs')) -const stream = require('stream') -const mime = require('./mime') -const handlers = require('./handlers') -const supportedFileTypes = require('./supported') -const errors = require('../../shared/errors') -const parsePartitions = require('./parse-partitions') - -/** - * @summary Get an image stream from a file - * @function - * @public - * - * @description - * This function resolves an object containing the following properties, - * along with various extra metadata: - * - * - `Number size`: The input file size. - * - * - `ReadableStream stream`: The input file stream. - * - * - `TransformStream transform`: A transform stream that performs any - * needed transformation to get the image out of the source input file - * (for example, decompression). - * - * The purpose of separating the above components is to handle cases like - * showing a progress bar when you can't know the final uncompressed size. - * - * In such case, you can pipe the `stream` through a progress stream using - * the input file `size`, and apply the `transform` after the progress stream. - * - * @param {String} file - file path - * @fulfil {Object} - image stream details - * @returns {Promise} - * - * @example - * const imageStream = require('./lib/sdk/image-stream'); - * - * imageStream.getFromFilePath('path/to/rpi.img.xz').then((image) => { - * console.log(`The image display name is: ${image.name}`); - * console.log(`The image url is: ${image.url}`); - * console.log(`The image support url is: ${image.supportUrl}`); - * console.log(`The image logo is: ${image.logo}`); - * - * image.stream - * .pipe(image.transform) - * .pipe(fs.createWriteStream('/dev/disk2')); - * }); - */ -exports.getFromFilePath = (file) => { - return fs.statAsync(file).then((fileStats) => { - if (!fileStats.isFile()) { - throw errors.createUserError({ - title: 'Invalid image', - description: 'The image must be a file' - }) - } - - return mime.getMimeTypeFromFileName(file).then((type) => { - const mimeType = _.has(handlers, type) ? type : mime.DEFAULT_MIME_TYPE - return _.invoke(handlers, mimeType, file, { - size: fileStats.size - }) - }) - }).then((image) => { - return _.omitBy(image, _.isUndefined) - }) -} - -/** - * @summary Get image metadata - * @function - * @public - * - * @description - * This function is useful to determine the final size of an image - * after decompression or any other needed transformation, as well as - * other relevant metadata, if any. - * - * **NOTE:** This function is known to output incorrect size results for - * `bzip2`. For this compression format, this function will simply - * return the size of the compressed file. - * - * @param {String} file - file path - * @fulfil {Object} - image metadata - * @returns {Promise} - * - * @example - * const imageStream = require('./lib/sdk/image-stream'); - * - * imageStream.getImageMetadata('path/to/rpi.img.xz').then((metadata) => { - * console.log(`The image display name is: ${metadata.name}`); - * console.log(`The image url is: ${metadata.url}`); - * console.log(`The image support url is: ${metadata.supportUrl}`); - * console.log(`The image logo is: ${metadata.logo}`); - * }); - */ -exports.getImageMetadata = (file) => { - return exports.getFromFilePath(file) - .then(parsePartitions) - .then((image) => { - return _.omitBy(image, (property) => { - return property instanceof stream.Stream || _.isNil(property) - }) - }) -} - -/** - * @summary Supported file types - * @type {String[]} - * @public - * - * @example - * const imageStream = require('./lib/sdk/image-stream'); - * - * imageStream.supportedFileTypes.forEach((fileType) => { - * console.log('Supported file type: ' + fileType.extension); - * }); - */ -exports.supportedFileTypes = supportedFileTypes diff --git a/lib/sdk/image-stream/mime.js b/lib/sdk/image-stream/mime.js deleted file mode 100644 index 6b032437..00000000 --- a/lib/sdk/image-stream/mime.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const fs = Bluebird.promisifyAll(require('fs')) -const fileType = require('file-type') -const mime = require('mime-types') -const utils = require('./utils') - -/** - * @summary The default MIME type - * @type {String} - * @constant - */ -exports.DEFAULT_MIME_TYPE = 'application/octet-stream' - -/** - * @summary Get file's mime type, by reading the initial 262 bytes if necessary - * @function - * @public - * - * @param {String} filename - file path - * @fulfil {String} - mime type - * @returns {Promise} - * - * @example - * mime.getMimeTypeFromFileName('path/to/raspberrypi.img.gz').then((mimeType) => { - * console.log(mimeType); - * }); - */ -exports.getMimeTypeFromFileName = (filename) => { - const mimeType = mime.lookup(filename) - - if (mimeType) { - return Bluebird.resolve(mimeType) - } - - const FILE_TYPE_ID_START = 0 - const FILE_TYPE_ID_BYTES = 262 - - return Bluebird.using(fs.openAsync(filename, 'r').disposer((fileDescriptor) => { - return fs.closeAsync(fileDescriptor) - }), (fileDescriptor) => { - return utils.readBufferFromImageFileDescriptor(fileDescriptor, FILE_TYPE_ID_START, FILE_TYPE_ID_BYTES).then((buffer) => { - return _.get(fileType(buffer), [ 'mime' ], exports.DEFAULT_MIME_TYPE) - }) - }) -} diff --git a/lib/sdk/image-stream/parse-partitions.js b/lib/sdk/image-stream/parse-partitions.js deleted file mode 100644 index 5d2ab3ed..00000000 --- a/lib/sdk/image-stream/parse-partitions.js +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const Bluebird = require('bluebird') -const MBR = require('mbr') -const GPT = require('gpt') - -/** - * @summary Maximum number of bytes to read from the stream - * @type {Number} - * @constant - */ -const MAX_STREAM_BYTES = 65536 - -/** - * @summary Initial number of bytes read - * @type {Number} - * @constant - */ -const INITIAL_LENGTH = 0 - -/** - * @summary Initial block size - * @type {Number} - * @constant - */ -const INITIAL_BLOCK_SIZE = 512 - -/** - * @summary Maximum block size to check for - * @type {Number} - * @constant - */ -const MAX_BLOCK_SIZE = 4096 - -/** - * @summary Attempt to parse the GPT from various block sizes - * @function - * @private - * - * @param {Buffer} buffer - Buffer - * @returns {GPT|null} - * - * @example - * const gpt = detectGPT(buffer); - * - * if (gpt != null) { - * // Has a GPT - * console.log('Partitions:', gpt.partitions); - * } - */ -const detectGPT = (buffer) => { - let blockSize = INITIAL_BLOCK_SIZE - let gpt = null - - // Attempt to parse the GPT from several offsets, - // as the block size of the image may vary (512,1024,2048,4096); - // For example, ISOs will usually have a block size of 4096, - // but raw images a block size of 512 bytes - while (blockSize <= MAX_BLOCK_SIZE) { - gpt = _.attempt(GPT.parse, buffer.slice(blockSize)) - if (!_.isError(gpt)) { - return gpt - } - blockSize += blockSize - } - - return null -} - -/** - * @summary Attempt to parse the MBR & GPT from a given buffer - * @function - * @private - * - * @param {Object} image - Image metadata - * @param {Buffer} buffer - Buffer - * - * @example - * parsePartitionTables(image, buffer); - * - * if (image.hasMBR || image.hasGPT) { - * console.log('Partitions:', image.partitions); - * } - */ -const parsePartitionTables = (image, buffer) => { - const mbr = _.attempt(MBR.parse, buffer) - let gpt = null - - if (!_.isError(mbr)) { - image.hasMBR = true - gpt = detectGPT(buffer) - image.hasGPT = !_.isNil(gpt) - } - - // As MBR and GPT partition entries have a different structure, - // we normalize them here to make them easier to deal with and - // avoid clutter in what's sent to analytics - if (image.hasGPT) { - image.partitions = _.map(gpt.partitions, (partition) => { - return { - type: partition.type.toString(), - id: partition.guid.toString(), - name: partition.name, - firstLBA: partition.firstLBA, - lastLBA: partition.lastLBA, - extended: false - } - }) - } else if (image.hasMBR) { - image.partitions = _.map(mbr.partitions, (partition) => { - return { - type: partition.type, - id: null, - name: null, - firstLBA: partition.firstLBA, - lastLBA: partition.lastLBA, - extended: partition.extended - } - }) - } -} - -/** - * @summary Attempt to read the MBR and GPT from an imagestream - * @function - * @public - * @description - * This operation will consume the first `MAX_STREAM_BYTES` - * of the stream and then destroy the stream. - * - * @param {Object} image - image metadata - * @returns {Promise} - * @fulfil {Object} image - * - * @example - * parsePartitions(image) - * .then((image) => { - * console.log('MBR:', image.hasMBR); - * console.log('GPT:', image.hasGPT); - * console.log('Partitions:', image.partitions); - * }); - */ -module.exports = (image) => { - return new Bluebird((resolve, reject) => { - const chunks = [] - let length = INITIAL_LENGTH - let destroyed = false - - image.hasMBR = false - image.hasGPT = false - - let stream = image.stream.pipe(image.transform) - - stream.on('error', reject) - - // We need to use the "old" flowing mode here, - // as some dependencies don't implement the "readable" - // mode properly (i.e. bzip2) - stream.on('data', (chunk) => { - chunks.push(chunk) - length += chunk.length - - // Once we've read enough bytes, terminate the stream - if (length >= MAX_STREAM_BYTES && !destroyed) { - // Prefer close() over destroy(), as some streams - // from dependencies exhibit quirky behavior when destroyed - if (image.stream.close) { - image.stream.close() - } else { - image.stream.destroy() - } - - // Remove references to stream to allow them being GCed - image.stream = null - image.transform = null - stream = null - destroyed = true - - // Parse the MBR, GPT and partitions from the obtained buffer - parsePartitionTables(image, Buffer.concat(chunks)) - resolve(image) - } - }) - }) -} diff --git a/lib/sdk/image-stream/supported.js b/lib/sdk/image-stream/supported.js deleted file mode 100644 index 9e2320da..00000000 --- a/lib/sdk/image-stream/supported.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -/** - * @summary Supported filename extensions - * @description - * NOTE: Extensions with type: 'compressed' will be stripped - * from filenames to determine the format of the uncompressed image. - * For details, see lib/image-stream/README.md - * @const {Array} - */ -module.exports = [ - { - extension: 'zip', - type: 'archive' - }, - { - extension: 'etch', - type: 'archive' - }, - { - extension: 'gz', - type: 'compressed' - }, - { - extension: 'bz2', - type: 'compressed' - }, - { - extension: 'xz', - type: 'compressed' - }, - { - extension: 'img', - type: 'image' - }, - { - extension: 'iso', - type: 'image' - }, - { - extension: 'bin', - type: 'image' - }, - { - extension: 'dsk', - type: 'image' - }, - { - extension: 'hddimg', - type: 'image' - }, - { - extension: 'raw', - type: 'image' - }, - { - extension: 'dmg', - type: 'image' - }, - { - extension: 'sdcard', - type: 'image' - }, - { - extension: 'rpi-sdimg', - type: 'image' - }, - { - extension: 'wic', - type: 'image' - } -] diff --git a/lib/sdk/image-stream/utils.js b/lib/sdk/image-stream/utils.js deleted file mode 100644 index 3b507f52..00000000 --- a/lib/sdk/image-stream/utils.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const Bluebird = require('bluebird') -const fs = Bluebird.promisifyAll(require('fs')) -const errors = require('../../shared/errors') - -/** - * @summary Read a buffer from an image file descriptor - * @function - * @private - * - * @param {Number} fileDescriptor - file descriptor - * @param {Number} position - image position to start reading from - * @param {Number} count - number of bytes to read - * @fulfil {Buffer} - buffer - * @returns {Promise} - * - * @example - * fs.openAsync('path/to/image.img', 'r').then((fileDescriptor) => { - * return utils.readBufferFromImageFileDescriptor(fileDescriptor, 0, 512); - * }).then((buffer) => { - * console.log(buffer); - * }); - */ -exports.readBufferFromImageFileDescriptor = (fileDescriptor, position, count) => { - const BUFFER_FILL_VALUE = 0 - const BUFFER_START_POSITION = 0 - const buffer = Buffer.alloc(count, BUFFER_FILL_VALUE) - - return fs.readAsync(fileDescriptor, buffer, BUFFER_START_POSITION, count, position).tap((bytesRead) => { - if (bytesRead !== count) { - throw errors.createUserError({ - title: 'Looks like the image is truncated', - description: `We tried to read ${count} bytes at ${position}, but got ${bytesRead} bytes instead` - }) - } - }).return(buffer) -} - -/** - * @summary Extract the data of a readable stream - * @function - * @public - * - * @description - * You should be careful when using this function, since you can only - * extract files that are not bigger than the available computer memory. - * - * @param {StreamReadable} stream - stream - * @fulfil {Buffer} - data - * @returns {Promise} - * - * @example - * const stream = fs.createReadStream('./foo/bar'); - * - * utils.extractStream(stream).then((data) => { - * console.log(data.toString()); - * }); - */ -exports.extractStream = (stream) => { - return new Bluebird((resolve, reject) => { - const chunks = [] - - stream.on('data', (chunk) => { - chunks.push(chunk) - }) - - stream.on('error', reject) - stream.on('end', () => { - resolve(Buffer.concat(chunks)) - }) - }) -} diff --git a/lib/sdk/index.js b/lib/sdk/index.js deleted file mode 100644 index 7a0dedb1..00000000 --- a/lib/sdk/index.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const SDK = module.exports - -/** - * @summary Initialised adapters - * @type {Object} - * @constant - */ -SDK.adapters = require('./adapters') - -/** - * Adapter Scanner - * @see scanner.js - * @ignore - */ -SDK.Scanner = require('./scanner') - -/** - * @summary Create a new Scanner - * @param {Object} [options] - options - * @returns {SDK.Scanner} - * @example - * SDK.createScanner({ - * blockdevice: { ... }, - * usbboot: { ... } - * }) - */ -SDK.createScanner = (options) => { - return new SDK.Scanner(options) -} diff --git a/lib/sdk/scanner.js b/lib/sdk/scanner.js deleted file mode 100644 index 06a0aeda..00000000 --- a/lib/sdk/scanner.js +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const _ = require('lodash') -const EventEmitter = require('events') -const debug = require('debug')('etcher:sdk:scanner') -const SDK = require('./') - -debug.enabled = true - -/* eslint-disable lodash/prefer-lodash-method */ - -/** - * Adapter Scanner - * @class Scanner - * @memberOf SDK - */ -class Scanner extends EventEmitter { - /** - * @summary Adapter Scanner constructor - * @param {Object} [options] - device adapter options - * @param {Object} [options.adapters] - map of external device adapters - * @example - * new Scanner({ - * blockdevice: { ... }, - * usbboot: { ... } - * }) - */ - constructor (options = {}) { - // Inherit from EventEmitter - super() - - this.options = options - this.isScanning = false - this.adapters = new Map() - - // Bind event handlers to own context to facilitate - // removing listeners by reference - this.onDevices = this.onDevices.bind(this) - this.onError = this.onError.bind(this) - - this.init() - } - - /** - * @summary Initialize adapters - * @private - * @example - * // Only to be used internally - * this.init() - */ - init () { - debug('scanner:init', this) - _.each(_.keys(this.options), (adapterId) => { - const adapter = SDK.adapters[adapterId] || - _.get(this.options, [ 'adapters', adapterId ]) - - if (_.isNil(adapter)) { - console.warn(`Unknown adapter "${adapterId}"`) - return - } - - this.subscribe(adapter) - }) - } - - /** - * @summary Event handler for adapter's "device" events - * @private - * @example - * adapter.on('devices', this.onDevices) - */ - onDevices () { - const devices = [] - this.adapters.forEach((adapter) => { - devices.push(...adapter.devices) - }) - this.emit('devices', devices) - } - - /** - * @summary Event handler for adapter's "error" events - * @param {Error} error - error - * @private - * @example - * adapter.on('error', this.onError) - */ - onError (error) { - this.emit('error', error) - } - - /** - * @summary Start scanning for devices - * @public - * @returns {Scanner} - * @example - * scanner.start() - */ - start () { - debug('start', !this.isScanning) - if (this.isScanning) { - return this - } - - this.adapters.forEach((adapter) => { - const options = this.options[adapter.id] - - /** - * @summary Run a scan with an adapter - * @function - * @private - * @example - * runScan() - */ - const runScan = () => { - adapter.scan(options, () => { - if (this.isScanning) { - setTimeout(runScan, Scanner.MIN_SCAN_DELAY) - } - }) - } - - adapter - .on('devices', this.onDevices) - .on('error', this.onError) - - runScan() - }) - - this.emit('start') - this.isScanning = true - - return this - } - - /** - * @summary Stop scanning for devices - * @public - * @returns {Scanner} - * @example - * scanner.stop() - */ - stop () { - debug('stop', this.isScanning) - if (!this.isScanning) { - return this - } - - this.adapters.forEach((adapter) => { - adapter.removeListener('devices', this.onDevices) - adapter.removeListener('error', this.onError) - }) - - this.isScanning = false - this.emit('stop') - - return this - } - - /** - * @summary Subscribe to an adapter - * @public - * @param {Adapter} adapter - device adapter - * @returns {Scanner} - * @example - * scanner.subscribe(adapter) - */ - subscribe (adapter) { - debug('subscribe', adapter) - - if (this.adapters.get(adapter.id)) { - throw new Error(`Scanner: Already subscribed to ${adapter.id}`) - } - - this.adapters.set(adapter.id, adapter) - this.emit('subscribe', adapter) - - return this - } - - /** - * @summary Unsubscribe from an adapter - * @public - * @param {Adapter} adapter - device adapter - * @returns {Scanner} - * @example - * scanner.unsubscribe(adapter) - * // OR - * scanner.unsubscribe('adapterName') - */ - unsubscribe (adapter) { - debug('unsubscribe', adapter) - const instance = _.isString(adapter) ? this.adapters.get(adapter) : this.adapters.get(adapter.id) - - if (_.isNil(instance)) { - // Not subscribed - return this - } - - instance.removeListener('devices', this.onDevices) - instance.removeListener('error', this.onError) - - this.adapters.delete(instance.id) - this.emit('unsubscribe', adapter) - - return this - } -} - -/** - * @summary Minimum delay between scans in ms - * @const - * @type {Number} - */ -Scanner.MIN_SCAN_DELAY = 500 - -module.exports = Scanner diff --git a/lib/sdk/writer/.eslintrc.yml b/lib/sdk/writer/.eslintrc.yml deleted file mode 100644 index 6b0ea6e9..00000000 --- a/lib/sdk/writer/.eslintrc.yml +++ /dev/null @@ -1,7 +0,0 @@ -rules: - no-eq-null: off - no-magic-numbers: off - no-param-reassign: off - no-underscore-dangle: off - lodash/prefer-lodash-method: off - lodash/prefer-get: off diff --git a/lib/sdk/writer/block-read-stream.js b/lib/sdk/writer/block-read-stream.js deleted file mode 100644 index 91dc16bc..00000000 --- a/lib/sdk/writer/block-read-stream.js +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const stream = require('readable-stream') -const fs = require('fs') -const debug = require('debug')('etcher:writer:block-read-stream') -const errors = require('./error-types') - -const CHUNK_SIZE = 64 * 1024 -const MIN_CHUNK_SIZE = 512 - -/** - * @summary I/O retry base timeout, in milliseconds - * @constant - * @type {Number} - */ -const RETRY_BASE_TIMEOUT = 100 - -/** - * @summary BlockReadStream - * @class - */ -class BlockReadStream extends stream.Readable { - /** - * @summary BlockReadStream constructor - * @param {Object} [options] - options - * @param {Number} [options.fd] - file descriptor - * @param {String} [options.path] - file path - * @param {String} [options.flags] - file open flags - * @param {Number} [options.mode] - file mode - * @param {Number} [options.start] - start offset in bytes - * @param {Number} [options.end] - end offset in bytes - * @param {Boolean} [options.autoClose] - automatically close the stream on end - * @param {Number} [options.maxRetries] - maximum number of retries per read - * @example - * new BlockReadStream() - */ - constructor (options) { - options = Object.assign({}, BlockReadStream.defaults, options) - options.objectMode = true - - debug('block-read-stream %j', options) - - super(options) - - this.fs = options.fs - this.fd = options.fd - this.path = options.path - this.flags = options.flags - this.mode = options.mode - this.end = options.end || Infinity - this.autoClose = options.autoClose - this.maxRetries = options.maxRetries || 5 - - this.retries = 0 - this.position = options.start || 0 - this.bytesRead = 0 - - this.closed = false - this.destroyed = false - - this.once('end', function () { - if (this.autoClose) { - this.close() - } - }) - - /** - * @summary onRead handler - * @param {Error} error - error - * @param {Number} bytesRead - bytes read - * @param {Buffer} buffer - resulting buffer - * @example - * fs.read(fd, buffer, 0, length, position, onRead) - */ - this._onRead = (error, bytesRead, buffer) => { - if (!error && bytesRead !== buffer.length) { - error = new Error(`Bytes read mismatch: ${bytesRead} != ${buffer.length}`) - } - - if (error) { - const isTransient = errors.isTransientError(error) - - if (isTransient && (this.retries < this.maxRetries)) { - this.retries += 1 - setTimeout(() => { - this._read() - }, RETRY_BASE_TIMEOUT * this.retries) - return - } else if (isTransient) { - error.code = 'EUNPLUGGED' - } - - if (this.autoClose) { - this.destroy() - } - - this.emit('error', error) - - return - } - - this.retries = 0 - this.bytesRead += bytesRead - this.position += buffer.length - this.push(buffer) - } - - this.open() - } - - /** - * @summary Read a chunk from the source - * @private - * @example - * // not to be called directly - */ - _read () { - // Wait for file handle to be open - if (this.fd == null) { - this.once('open', () => { - this._read() - }) - return - } - - const toRead = this.end - this.position - - if (toRead <= 0) { - this.push(null) - return - } - - const length = Math.min(CHUNK_SIZE, Math.max(MIN_CHUNK_SIZE, toRead)) - const buffer = Buffer.alloc(length) - - this.fs.read(this.fd, buffer, 0, length, this.position, this._onRead) - } - - /** - * @summary Open a handle to the file - * @private - * @example - * this.open() - */ - open () { - debug('open') - - if (this.fd != null) { - this.emit('open', this.fd) - return - } - - this.fs.open(this.path, this.flags, this.mode, (error, fd) => { - if (error) { - if (this.autoClose) { - this.destroy() - } - this.emit('error', error) - } else { - this.fd = fd - this.emit('open', fd) - } - }) - } - - /** - * @summary Close the underlying resource - * @param {Function} callback - callback(error) - * @example - * blockStream.close((error) => { - * // ... - * }) - */ - close (callback) { - debug('close') - - if (callback) { - this.once('close', callback) - } - - if (this.closed || this.fd == null) { - if (this.fd == null) { - this.once('open', () => { - this.close() - }) - } else { - process.nextTick(() => { - this.emit('close') - }) - } - return - } - - this.closed = true - - this.fs.close(this.fd, (error) => { - if (error) { - this.emit('error', error) - } else { - this.emit('close') - } - }) - - this.fd = null - } -} - -/** - * @summary Default options - * @type {Object} - * @constant - */ -BlockReadStream.defaults = { - fs, - fd: null, - path: null, - flags: 'r', - mode: 0o666, - autoClose: true -} - -module.exports = BlockReadStream diff --git a/lib/sdk/writer/block-stream.js b/lib/sdk/writer/block-stream.js deleted file mode 100644 index 67a71c7b..00000000 --- a/lib/sdk/writer/block-stream.js +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const stream = require('readable-stream') -const debug = require('debug')('etcher:writer:block-stream') - -const MIN_BLOCK_SIZE = 512 -const CHUNK_SIZE = 64 * 1024 - -/** - * @summary BlockStream class - * @class - */ -class BlockStream extends stream.Transform { - /** - * @summary BlockStream constructor - * @param {Object} [options] - options - * @param {Number} [options.blockSize] - block size in bytes - * @param {Number} [options.chunkSize] - chunk size in bytes - * @example - * new BlockStream(options) - */ - constructor (options) { - options = Object.assign({}, BlockStream.defaults, options) - options.readableObjectMode = true - - super(options) - - this.blockSize = options.blockSize - this.chunkSize = options.chunkSize - this.bytesRead = 0 - this.bytesWritten = 0 - - this._buffers = [] - this._bytes = 0 - - debug('new %j', options) - } - - /** - * @summary Internal write handler - * @private - * @param {Buffer} chunk - chunk buffer - * @param {String} encoding - chunk encoding - * @param {Function} next - callback(error, value) - * @example - * // Not to be called directly - */ - _transform (chunk, encoding, next) { - this.bytesRead += chunk.length - - if (this._bytes === 0 && chunk.length >= this.chunkSize) { - if (chunk.length % this.blockSize === 0) { - this.bytesWritten += chunk.length - this.push(chunk) - next() - return - } - } - - this._buffers.push(chunk) - this._bytes += chunk.length - - if (this._bytes >= this.chunkSize) { - let block = Buffer.concat(this._buffers) - const length = Math.floor(block.length / this.blockSize) * this.blockSize - - this._buffers.length = 0 - this._bytes = 0 - - if (block.length !== length) { - this._buffers.push(block.slice(length)) - this._bytes += block.length - length - block = block.slice(0, length) - } - - this.bytesWritten += block.length - this.push(block) - } - - next() - } - - /** - * @summary Internal stream end handler - * @private - * @param {Function} done - callback(error, value) - * @example - * // Not to be called directly - */ - _flush (done) { - if (!this._bytes) { - done() - return - } - - const length = Math.ceil(this._bytes / this.blockSize) * this.blockSize - const block = Buffer.alloc(length) - let offset = 0 - - for (let index = 0; index < this._buffers.length; index += 1) { - this._buffers[index].copy(block, offset) - offset += this._buffers[index].length - } - - this.push(block) - done() - } -} - -/** - * @summary Default options - * @type {Object} - * @constant - */ -BlockStream.defaults = { - blockSize: MIN_BLOCK_SIZE, - chunkSize: CHUNK_SIZE -} - -module.exports = BlockStream diff --git a/lib/sdk/writer/block-write-stream.js b/lib/sdk/writer/block-write-stream.js deleted file mode 100644 index 597be99a..00000000 --- a/lib/sdk/writer/block-write-stream.js +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const stream = require('readable-stream') -const fs = require('fs') -const speedometer = require('speedometer') -const debug = require('debug')('etcher:writer:block-write-stream') -const errors = require('./error-types') - -const CHUNK_SIZE = 64 * 1024 -const UPDATE_INTERVAL_MS = 500 - -/** - * @summary I/O retry base timeout, in milliseconds - * @constant - * @type {Number} - */ -const RETRY_BASE_TIMEOUT = 100 - -/** - * @summary BlockWriteStream - * @class - */ -class BlockWriteStream extends stream.Writable { - /** - * @summary BlockWriteStream constructor - * @param {Object} [options] - options - * @param {Number} [options.fd] - file descriptor - * @param {String} [options.path] - file path - * @param {String} [options.flags] - file open flags - * @param {Number} [options.mode] - file mode - * @param {Boolean} [options.autoClose] - automatically close the stream on end - * @param {Number} [options.maxRetries] - maximum number of retries per write - * @example - * new BlockWriteStream(options) - */ - constructor (options) { - options = Object.assign({}, BlockWriteStream.defaults, options) - options.objectMode = true - - debug('block-write-stream %j', options) - - super(options) - - this._writableState.highWaterMark = 1 - - this.fs = options.fs - this.fd = options.fd - this.path = options.path - this.flags = options.flags - this.mode = options.mode - this.autoClose = options.autoClose - this.maxRetries = options.maxRetries || 5 - - this.position = 0 - this.bytesRead = 0 - this.blocksRead = 0 - this.bytesWritten = 0 - this.blocksWritten = 0 - this.retries = 0 - this.meter = speedometer() - this.delta = 0 - this.speed = 0 - - this.clear = () => { - clearInterval(this.timer) - } - - this.update = () => { - this.speed = this.meter(this.delta) - this.delta = 0 - } - - this.once('end', this.clear) - this.once('error', this.clear) - - this.timer = setInterval(this.update, UPDATE_INTERVAL_MS) - - this.closed = false - this.destroyed = false - - this.once('finish', function () { - if (this.autoClose) { - this.close() - } - }) - - this._flushing = false - this._firstBlocks = [] - - this.open() - } - - /** - * @summary Internal write handler - * @private - * @param {Buffer} chunk - chunk buffer - * @param {String} encoding - chunk encoding - * @param {Function} next - callback(error, value) - * @example - * // Not to be called directly - */ - _write (chunk, encoding, next) { - debug('_write', chunk.length, chunk.position, chunk.address) - - // Wait for file handle to be open - if (this.fd == null) { - this.once('open', () => { - this._write(chunk, encoding, next) - }) - return - } - - if (this.retries === 0) { - this.bytesRead += chunk.length - this.blocksRead += 1 - } - - if (chunk.position == null) { - chunk.position = this.position - } - - if (!this._flushing && (chunk.position < CHUNK_SIZE)) { - this._firstBlocks.push(chunk) - this.position = chunk.position + chunk.length - process.nextTick(next) - return - } - - if (chunk.position !== this.position) { - this.position = chunk.position - } - - fs.write(this.fd, chunk, 0, chunk.length, chunk.position, (error, bytesWritten) => { - if (!error) { - this.bytesWritten += bytesWritten - this.delta += bytesWritten - this.blocksWritten += 1 - this.position += bytesWritten - this.retries = 0 - next() - return - } - - const isTransient = errors.isTransientError(error) - - if (isTransient && (this.retries < this.maxRetries)) { - this.retries += 1 - setTimeout(() => { - this._write(chunk, encoding, next) - }, RETRY_BASE_TIMEOUT * this.retries) - return - } else if (isTransient) { - error.code = 'EUNPLUGGED' - } - - next(error) - }) - } - - /** - * @summary Write buffered data before a stream ends - * @private - * @param {Function} done - callback - * @example - * // Called by stream internals - */ - _final (done) { - debug('_final') - - /** - * @summary Write the next chunk of the buffered `_firstBlocks` - * @param {Error} [error] - error - * @example - * writeNext() - */ - const writeNext = (error) => { - if (error) { - this.destroy(error) - return - } - const chunk = this._firstBlocks.pop() - if (!chunk) { - done() - return - } - this._write(chunk, null, writeNext) - } - - this._flushing = true - writeNext() - } - - /** - * @summary Destroy the stream, and emit the passed error - * @private - * @param {Error} [error] - error - * @param {Function} done - callback - * @example - * stream.destroy() - */ - _destroy (error, done) { - debug('_destroy', error) - - if (this.autoClose) { - this.close((closeError) => { - done(error || closeError) - }) - } else { - done(error) - } - } - - /** - * @summary Open a handle to the file - * @private - * @example - * this.open() - */ - open () { - debug('open') - - if (this.fd != null) { - this.emit('open', this.fd) - return - } - - this.fs.open(this.path, this.flags, this.mode, (error, fd) => { - if (error) { - if (this.autoClose) { - this.destroy() - } - this.emit('error', error) - } else { - this.fd = fd - this.emit('open', fd) - } - }) - } - - /** - * @summary Close the underlying resource - * @param {Function} callback - callback(error) - * @example - * blockStream.close((error) => { - * // ... - * }) - */ - close (callback) { - debug('close') - - if (callback) { - this.once('close', callback) - } - - if (this.closed || this.fd == null) { - if (this.fd == null) { - this.once('open', () => { - this.close() - }) - } else { - process.nextTick(() => { - this.emit('close') - }) - } - return - } - - this.closed = true - - this.fs.close(this.fd, (error) => { - if (error) { - this.emit('error', error) - } else { - this.emit('close') - } - }) - - this.fd = null - } -} - -/** - * @summary Default options - * @type {Object} - * @constant - */ -BlockWriteStream.defaults = { - fs, - fd: null, - path: null, - flags: 'w', - mode: 0o666, - autoClose: true -} - -module.exports = BlockWriteStream diff --git a/lib/sdk/writer/checksum-stream.js b/lib/sdk/writer/checksum-stream.js deleted file mode 100644 index 16f0c769..00000000 --- a/lib/sdk/writer/checksum-stream.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const stream = require('readable-stream') -const crypto = require('crypto') -const xxhash = require('xxhash') -const _ = require('lodash') - -/** - * @summary Create an instance of ChecksumStream - * @name ChecksumStream - * @class - */ -class ChecksumStream extends stream.Transform { - /** - * @summary Create an instance of ChecksumStream - * @name ChecksumStream - * @class - * @param {Object} options - options - * @param {String[]} options.algorithms - hash algorithms - * @example - * var checksum = new ChecksumStream({ - * algorithms: [ 'md5' ] - * }) - * - * checksum.once('checksum', (checksum) => { - * // checksum: { - * // md5: '55a4eb779e08f604c41ba1cbfff47ada' - * // } - * }) - * - * fs.createReadStream( 'os-image.img' ) - * .pipe( checksum ) - * .pipe( fs.createWriteStream( '/dev/rdisk2' ) ) - * .once( 'finish', () => { ... }) - */ - constructor (options = {}) { - super(options) - this.results = {} - this.algorithms = options.algorithms || [] - this.hashes = _.map(this.algorithms, (algorithm) => { - return this._createHash(algorithm) - }) - } - - /** - * @summary Create & pipe to the Hash streams - * @private - * @param {String[]} algorithm - hash algorithm - * @returns {Stream} - * @example - * const hash = this._createHash(algorithm) - */ - _createHash (algorithm) { - let hash = null - - if (algorithm === 'xxhash') { - // Seed value 0x45544348 = ASCII "ETCH" - const seed = 0x45544348 - const is64Bit = [ 'x64', 'arm64', 'ppc64' ].includes(process.arch) - hash = new xxhash.Stream(seed, is64Bit ? 64 : 32, Buffer.allocUnsafe(is64Bit ? 8 : 4)) - } else { - hash = _.attempt(crypto.createHash, algorithm) - } - - if (_.isError(hash)) { - hash.message += ` "${algorithm}"` - throw hash - } - - /** - * @summary Check for all checksums to have been calculated - * @private - * @example - * hash.once('end', check) - */ - const check = () => { - if (_.keys(this.results).length === this.algorithms.length) { - this.emit('checksum', _.clone(this.results)) - } - } - - hash.once('error', (error) => { - return this.emit('error', error) - }) - - hash.once('readable', () => { - this.results[algorithm] = hash.read().toString('hex') - check() - }) - - return hash - } - - /** - * @summary Pass through chunks - * @private - * @param {Buffer} chunk - chunk - * @param {String} encoding - encoding - * @param {Function} next - callback - * @example - * checksumStream.write(buffer) - */ - _transform (chunk, encoding, next) { - for (let index = 0; index < this.hashes.length; index += 1) { - this.hashes[index].write(chunk) - } - next(null, chunk) - } - - /** - * @summary End the hash streams once this stream ends - * @private - * @param {Function} done - callback - * @example - * checksumStream.end() - */ - _flush (done) { - for (let index = 0; index < this.hashes.length; index += 1) { - this.hashes[index].end() - } - done() - } -} - -module.exports = ChecksumStream diff --git a/lib/sdk/writer/error-types.js b/lib/sdk/writer/error-types.js deleted file mode 100644 index 87724775..00000000 --- a/lib/sdk/writer/error-types.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -module.exports = { - - /** - * @summary Determine whether an error is considered a - * transient occurrence, and the operation should be retried - * Errors considered potentially temporary are: - * - Mac OS: ENXIO, EBUSY - * - Windows: ENOENT, UNKNOWN - * - Linux: EIO, EBUSY - * @private - * @param {Error} error - Error - * @returns {Boolean} - * @example - * errors.isTransientError(error) - */ - isTransientError (error) { - if (process.platform === 'darwin') { - return error.code === 'ENXIO' || error.code === 'EBUSY' - } else if (process.platform === 'linux') { - return error.code === 'EIO' || error.code === 'EBUSY' - } else if (process.platform === 'win32') { - return error.code === 'ENOENT' || error.code === 'UNKNOWN' - } - return false - } - -} diff --git a/lib/sdk/writer/index.js b/lib/sdk/writer/index.js deleted file mode 100644 index 177c5162..00000000 --- a/lib/sdk/writer/index.js +++ /dev/null @@ -1,832 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const os = require('os') -const fs = require('fs') -const EventEmitter = require('events').EventEmitter -const mountutils = require('mountutils') -const drivelist = require('drivelist') -const stream = require('readable-stream') -const Pipage = require('pipage') -const BlockMap = require('blockmap') -const BlockStream = require('./block-stream') -const BlockWriteStream = require('./block-write-stream') -const BlockReadStream = require('./block-read-stream') -const ChecksumStream = require('./checksum-stream') -const ProgressStream = require('./progress-stream') -const imageStream = require('../image-stream') -const diskpart = require('../../cli/diskpart') -const constraints = require('../../shared/drive-constraints') -const errors = require('../../shared/errors') -const debug = require('debug')('etcher:writer') -const _ = require('lodash') - -/* eslint-disable prefer-reflect */ -/* eslint-disable callback-return */ - -/** - * @summary Timeout, in milliseconds, to wait before unmounting on success - * @constant - * @type {Number} - */ -const UNMOUNT_ON_SUCCESS_TIMEOUT_MS = 2000 - -/** - * @summary Helper function to run a set of async tasks in sequence - * @private - * @param {Array} tasks - set of tasks - * @param {Function} callback - callback(error) - * @example - * runSeries([ - * (next) => first(next), - * (next) => second(next), - * ], (error) => { - * // ... - * }) - */ -const runSeries = (tasks, callback) => { - /** - * @summary Task runner - * @param {Error} [error] - error - * @example - * run() - */ - const run = (error) => { - const task = tasks.shift() - if (error || task == null) { - callback(error) - return - } - task(run) - } - - run() -} - -/** - * @summary Helper function to run a set of async tasks in sequence - * @private - * @param {Array} tasks - set of tasks - * @param {Function} callback - callback(error) - * @example - * runParallel([ - * (next) => first(next), - * (next) => second(next), - * ], (error) => { - * // ... - * }) - */ -const runParallel = (tasks, callback) => { - let count = tasks.length - const resultErrors = new Array(count).fill(null) - const results = new Array(count).fill(null) - - tasks.forEach((task, index) => { - task((error, result) => { - count -= 1 - resultErrors[index] = error - results[index] = result - if (count === 0) { - callback(resultErrors, results) - } - }) - }) -} - -/** - * @summary ImageWriter class - * @class - */ -class ImageWriter extends EventEmitter { - /** - * @summary ImageWriter constructor - * @param {Object} options - options - * @param {Boolean} options.verify - whether to verify the dest - * @param {Boolean} options.unmountOnSuccess - whether to unmount the dest after flashing - * @param {Array} options.checksumAlgorithms - checksums to calculate - * @example - * new ImageWriter(options) - */ - constructor (options) { - options = options || {} - super() - - debug('new', options) - - this.unmountOnSuccess = Boolean(options.unmountOnSuccess) - this.verifyChecksums = Boolean(options.verify) - this.checksumAlgorithms = options.checksumAlgorithms || [] - - this.source = null - this.pipeline = null - this.destinations = new Map() - - this.finished = false - this.hadError = false - - this.bytesRead = 0 - this.bytesWritten = 0 - this.checksum = {} - - this.once('error', () => { - this.hadError = true - }) - } - - /** - * @summary Verify that the selected destination devices exist - * @param {Array} paths - target device paths - * @param {Function} callback - callback(error) - * @private - * @example - * writer.getSelectedDevices(['/dev/disk2'], (error, destinations) => { - * // ... - * }) - */ - getSelectedDevices (paths, callback) { - debug('state:device-select', paths) - drivelist.list((error, drives) => { - debug('state:device-select', paths, error ? 'NOT OK' : 'OK') - - if (error) { - callback.call(this, error) - return - } - - const results = paths.map((path) => { - const destination = { - fd: null, - error: null, - stream: null, - finished: false, - verified: false, - device: _.find(drives, { - device: path - }) - } - - if (!destination.device) { - const selectionError = errors.createUserError({ - title: `The selected drive "${path}" was not found`, - description: `We can't find "${path}" in your system. Did you unplug the drive?`, - code: 'EUNPLUGGED' - }) - debug('state:device-select', destination, 'NOT OK') - destination.error = selectionError - } - - return destination - }) - - callback.call(this, null, results) - }) - } - - /** - * @summary Unmount the destination device - * @param {Object} destination - destination object - * @param {Function} callback - callback(error) - * @private - * @example - * writer.unmountDevice((error) => { - * // ... - * }) - */ - unmountDevice (destination, callback) { - if (os.platform() === 'win32') { - callback.call(this) - return - } - - debug('state:unmount', destination.device.device) - - mountutils.unmountDisk(destination.device.device, (error) => { - debug('state:unmount', destination.device.device, error ? 'NOT OK' : 'OK') - destination.error = error - callback.call(this, error) - }) - } - - /** - * @summary Clean a device's partition table - * @param {Object} destination - destination object - * @param {Function} callback - callback(error) - * @private - * @example - * writer.removePartitionTable((error) => { - * // ... - * }) - */ - removePartitionTable (destination, callback) { - if (os.platform() !== 'win32') { - callback.call(this) - return - } - - debug('state:clean', destination.device.device) - - diskpart.clean(destination.device.device).asCallback((error) => { - debug('state:clean', destination.device.device, error ? 'NOT OK' : 'OK') - destination.error = error - callback.call(this, error) - }) - } - - /** - * @summary Open the source for reading - * @param {String} imagePath - path to source image - * @param {Function} callback - callback(error) - * @private - * @example - * writer.openSource('path/to/image.img', (error, source) => { - * // ... - * }) - */ - openSource (imagePath, callback) { - debug('state:source-open', imagePath) - imageStream.getFromFilePath(imagePath).asCallback((error, image) => { - debug('state:source-open', imagePath, error ? 'NOT OK' : 'OK') - this.source = image - callback.call(this, error, this.source) - }) - } - - /** - * @summary Open the destination for writing - * @param {Object} destination - destination object - * @param {Function} callback - callback(error) - * @private - * @example - * writer.openDestination((error) => { - * // ... - * }) - */ - openDestination (destination, callback) { - debug('state:destination-open', destination.device.raw) - - /* eslint-disable no-bitwise */ - const flags = fs.constants.O_RDWR | - fs.constants.O_NONBLOCK | - fs.constants.O_SYNC - /* eslint-enable no-bitwise */ - - fs.open(destination.device.raw, flags, (error, fd) => { - debug('state:destination-open', destination.device.raw, error ? 'NOT OK' : 'OK') - destination.fd = fd - destination.error = error - callback.call(this, error) - }) - } - - /** - * @summary Check a destination against the drive constraints - * @param {Object} destination - destination object - * @param {Function} callback - callback(error) - * @example - * this.checkDriveConstraints(destination, (error) => { - * // ... - * }) - */ - checkDriveConstraints (destination, callback) { - if (!constraints.isDriveLargeEnough(destination.device, this.source)) { - destination.error = errors.createUserError({ - title: 'The image you selected is too big for this drive', - description: 'Please connect a bigger drive and try again' - }) - } - - callback.call(this, destination.error) - } - - /** - * @summary Start the flashing process - * @param {String} imagePath - path to source image - * @param {Array} destinationPaths - paths to target devices - * @returns {ImageWriter} imageWriter - * @example - * imageWriter.write(source, destinations) - * .on('error', reject) - * .on('progress', onProgress) - * .on('finish', resolve) - */ - write (imagePath, destinationPaths) { - // Open the source image - this.openSource(imagePath, (openError, source) => { - if (openError) { - this.emit('error', openError) - return - } - - // Open & prepare target devices - this.getSelectedDevices(destinationPaths, (error, destinations) => { - if (error) { - this.emit('error', error) - return - } - - const notFound = _.find(destinations, (destination) => { - return Boolean(destination.error) - }) - - if (notFound) { - this.emit('error', notFound.error) - return - } - - // Generate preparation tasks for all destinations - const tasks = destinations.map((destination) => { - destination.verified = !this.verifyChecksums - this.destinations.set(destination.device.device, destination) - return (next) => { - runSeries([ - (done) => { this.checkDriveConstraints(destination, done) }, - (done) => { this.unmountDevice(destination, done) }, - (done) => { this.removePartitionTable(destination, done) }, - (done) => { this.openDestination(destination, done) } - ], () => { - if (destination.error) { - this.emit('fail', { device: destination.device.device, error: destination.error }) - } - next(destination.error, destination) - }) - } - }) - - // Run the preparation tasks in parallel for each destination - runParallel(tasks, (resultErrors, results) => { - // We can start (theoretically) flashing now... - debug('write:prep:done', resultErrors) - if (_.every(resultErrors, _.identity)) { - this.emit('error', resultErrors[0]) - } else { - this._write() - } - }) - }) - }) - - return this - } - - /** - * @summary Internal progress state handler - * @param {Object} state - progress state - * @example - * pipeline.on('progress', (state) => { - * // ... - * this._onProgress(state) - * }) - */ - _onProgress (state) { - state.totalSpeed = 0 - state.active = 0 - - state.flashing = 0 - state.verifying = 0 - state.failed = 0 - state.successful = 0 - - this.destinations.forEach((dest) => { - state.flashing += !dest.error && !dest.finished ? 1 : 0 - state.verifying += !dest.error && dest.finished && !dest.verified ? 1 : 0 - state.failed += dest.error ? 1 : 0 - state.successful += !dest.error && dest.finished && (dest.verified || !this.verifyChecksums) ? 1 : 0 - if (!(dest.finished && dest.verified) && !dest.error) { - state.totalSpeed += state.type === 'write' - ? (dest.stream.speed || 0) - : (dest.progress.state.speed || 0) - state.active += 1 - } - }) - - state.speed = state.active - ? state.totalSpeed / state.active - : state.totalSpeed - - state.eta = state.speed ? state.remaining / state.speed : 0 - - this.emit('progress', state) - } - - /** - * @summary Start the writing process - * @returns {ImageWriter} imageWriter - * @example - * imageWriter.write() - */ - _write () { - this.pipeline = this._createWritePipeline() - - this.pipeline.on('checksum', (checksum) => { - debug('write:checksum', checksum) - this.checksum = checksum - }) - - this.pipeline.on('error', (error) => { - this.emit('error', error) - }) - - this.pipeline.on('complete', (destination) => { - this.bytesRead = this.source.bytesRead - - let finishedCount = 0 - let errorCount = 0 - - this.destinations.forEach((dest) => { - finishedCount += dest.finished ? 1 : 0 - errorCount += dest.error ? 1 : 0 - }) - - debug('write:finish', finishedCount, '/', this.destinations.size) - - if (_.has(destination, [ 'stream' ])) { - this.bytesWritten += destination.stream.bytesWritten - } - - if (finishedCount === this.destinations.size) { - if (errorCount === this.destinations.size) { - this.emit('error', destination.error) - this._finish() - } else if (this.verifyChecksums) { - debug('write:verify') - this.verify() - } else { - debug('write:finish') - this._finish() - } - } - }) - - return this - } - - /** - * @summary Start the writing process - * @returns {ImageWriter} imageWriter - * @example - * imageWriter.verify() - */ - verify () { - let bytesWritten = 0 - - // NOTE: We can't re-use `this.bytesWritten` here, as that will - // included bytes of streams that may have errored part way through - this.destinations.forEach((destination) => { - // Don't count errored destinations - if (destination.error || !destination.stream) { - return - } - bytesWritten += destination.stream.bytesWritten - }) - - const progressStream = new ProgressStream({ - length: bytesWritten, - time: 500 - }) - - progressStream.resume() - - progressStream.on('progress', (state) => { - state.type = 'check' - this._onProgress(state) - }) - - this.destinations.forEach((destination) => { - // Don't verify errored destinations - if (destination.error || !destination.stream) { - return - } - - const pipeline = this._createVerifyPipeline(destination) - - pipeline.on('error', (error) => { - // NOTE: As the `blockmap` module doesn't set our custom error codes, - // we need to patch `EVALIDATION` into a range checksum error here - if (error.message && error.message.startsWith('Invalid checksum for range')) { - error.code = 'EVALIDATION' - this.emit('fail', { device: destination.device.device, error }) - } - this.emit('error', error) - }) - - pipeline.on('checksum', (checksum) => { - debug('verify:checksum', this.checksum, '==', checksum) - destination.checksum = checksum - if (!_.isEqual(this.checksum, checksum)) { - const error = new Error(`Verification failed: ${JSON.stringify(this.checksum)} != ${JSON.stringify(checksum)}`) - error.code = 'EVALIDATION' - destination.error = error - this.emit('fail', { device: destination.device.device, error }) - } - }) - - pipeline.on('finish', () => { - debug('verify:finish') - - destination.verified = true - destination.progress = null - destination.stream = null - - let finishedCount = 0 - - this.destinations.forEach((dest) => { - finishedCount += (dest.error || dest.verified) ? 1 : 0 - }) - - if (finishedCount === this.destinations.size) { - debug('verify:complete') - progressStream.end() - this._finish() - } - }) - - // NOTE: Normally we'd use `pipeline.pipe(progressStream)` here, - // but that leads to degraded performance - pipeline.on('readable', function () { - let chunk = null - while ((chunk = this.read())) { - progressStream.write(chunk) - } - }) - }) - - return this - } - - /** - * @summary Abort the flashing process - * @example - * imageWriter.abort() - */ - abort () { - if (this.source && this.source.stream) { - this.source.stream.destroy() - } - this.emit('abort') - } - - /** - * @summary Cleanup after writing; close file descriptors & unmount - * @param {Function} callback - callback(error) - * @private - * @example - * writer._cleanup((error) => { - * // ... - * }) - */ - _cleanup (callback) { - debug('state:cleanup') - const tasks = [] - - this.destinations.forEach((destination) => { - tasks.push((next) => { - runSeries([ - (done) => { - if (destination.fd) { - fs.close(destination.fd, done) - destination.fd = null - } else { - done() - } - }, - (done) => { - if (!this.unmountOnSuccess) { - done() - return - } - - // Closing a file descriptor on a drive containing mountable - // partitions causes macOS to mount the drive. If we try to - // unmount too quickly, then the drive might get re-mounted - // right afterwards. - setTimeout(() => { - mountutils.unmountDisk(destination.device.device, (error) => { - debug('state:cleanup', error ? 'NOT OK' : 'OK') - done(error) - }) - }, UNMOUNT_ON_SUCCESS_TIMEOUT_MS) - } - ], next) - }) - }) - - runParallel(tasks, (resultErrors, results) => { - debug('state:cleanup', resultErrors) - callback.call(this, resultErrors) - }) - } - - /** - * @summary Emits the `finish` event with state metadata - * @private - * @example - * this._finish() - */ - _finish () { - this._cleanup(() => { - const failures = [] - let successful = 0 - let failed = 0 - - this.finished = true - - this.destinations.forEach((dest) => { - successful += dest.finished && dest.verified && !dest.error ? 1 : 0 - failed += dest.error ? 1 : 0 - if (dest.error) { - dest.error.device = dest.device.device - failures.push(dest.error) - } - }) - - this.emit('finish', { - devices: { successful, failed }, - bytesRead: this.bytesRead, - bytesWritten: this.bytesWritten, - checksum: this.checksum, - errors: failures - }) - }) - } - - /** - * @summary Creates a write pipeline from given options - * @private - * @returns {Pipage} pipeline - * @example - * this._createWritePipeline() - */ - _createWritePipeline () { - const pipeline = new Pipage({ - readableObjectMode: true - }) - - const progressOptions = { - length: this.source.size.original, - time: 500 - } - - let progressStream = null - - // If the final size is an estimation, - // use the original source size for progress metering - if (this.source.size.final.estimation) { - progressStream = new ProgressStream(progressOptions) - pipeline.append(progressStream) - } - - const isPassThrough = this.source.transform instanceof stream.PassThrough - - // If the image transform is a pass-through, - // ignore it to save on the overhead - if (this.source.transform && !isPassThrough) { - pipeline.append(this.source.transform) - } - - // If the final size is known precisely and we're not - // using block maps, then use the final size for progress - if (!this.source.size.final.estimation && !this.source.bmap) { - progressOptions.length = this.source.size.final.value - progressStream = new ProgressStream(progressOptions) - pipeline.append(progressStream) - } - - if (this.source.bmap) { - const blockMap = BlockMap.parse(this.source.bmap) - debug('write:bmap', blockMap) - progressStream = new ProgressStream(progressOptions) - pipeline.append(progressStream) - pipeline.append(new BlockMap.FilterStream(blockMap)) - } else { - debug('write:blockstream') - pipeline.append(new BlockStream()) - if (this.verifyChecksums) { - const checksumStream = new ChecksumStream({ - objectMode: true, - algorithms: this.checksumAlgorithms - }) - pipeline.append(checksumStream) - pipeline.bind(checksumStream, 'checksum') - } - } - - this.destinations.forEach((destination) => { - if (destination.error) { - debug('pipeline:skip', destination.device.device) - destination.finished = true - return - } - - destination.stream = new BlockWriteStream({ - fd: destination.fd, - autoClose: false - }) - - destination.stream.on('finish', () => { - debug('finish:unpipe', destination.device.device) - destination.finished = true - pipeline.emit('complete', destination) - pipeline.unpipe(destination.stream) - }) - - destination.stream.on('error', (error) => { - debug('error:unpipe', destination.device.device) - destination.error = error - destination.finished = true - pipeline.unpipe(destination.stream) - this.emit('fail', { device: destination.device.device, error }) - pipeline.emit('complete', destination) - }) - - pipeline.pipe(destination.stream) - }) - - // Pipeline.bind(progressStream, 'progress'); - progressStream.on('progress', (state) => { - state.type = 'write' - this._onProgress(state) - }) - - pipeline.bind(this.source.stream, 'error') - this.source.stream.pipe(pipeline) - - return pipeline - } - - /** - * @summary Creates a verification pipeline from given options - * @private - * @param {Object} destination - the destination object - * @returns {Pipage} pipeline - * @example - * this._createVerifyPipeline() - */ - _createVerifyPipeline (destination) { - const pipeline = new Pipage() - - let size = destination.stream.bytesWritten - - if (!this.source.size.final.estimation) { - size = Math.max(size, this.source.size.final.value) - } - - const progressStream = new ProgressStream({ - length: size, - time: 500 - }) - - pipeline.append(progressStream) - - if (this.source.bmap) { - debug('verify:bmap') - const blockMap = BlockMap.parse(this.source.bmap) - const blockMapStream = new BlockMap.FilterStream(blockMap) - pipeline.append(blockMapStream) - - // NOTE: Because the blockMapStream checksums each range, - // and doesn't emit a final "checksum" event, we artificially - // raise one once the stream finishes - blockMapStream.once('finish', () => { - pipeline.emit('checksum', {}) - }) - } else { - const checksumStream = new ChecksumStream({ - algorithms: this.checksumAlgorithms - }) - pipeline.append(checksumStream) - pipeline.bind(checksumStream, 'checksum') - } - - const source = new BlockReadStream({ - fd: destination.fd, - autoClose: false, - start: 0, - end: size, - highWaterMark: 1048576 - }) - - pipeline.bind(source, 'error') - - destination.stream = source.pipe(pipeline) - destination.progress = progressStream - - return pipeline - } -} - -module.exports = ImageWriter diff --git a/lib/sdk/writer/progress-stream.js b/lib/sdk/writer/progress-stream.js deleted file mode 100644 index b1edae98..00000000 --- a/lib/sdk/writer/progress-stream.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const Stream = require('stream') -const speedometer = require('speedometer') - -const PERCENT = 100 -const DEFAULT_TIME_MS = 500 - -/** - * @class ProgressStream - * @public - */ -class ProgressStream extends Stream.Transform { - /** - * @summary ProgressStream constructor - * @param {Object} options - options - * @param {Number} options.length - expected total - * @param {Number} [options.time] - time interval to report progress - * @example - * new ProgressStream({ length: 1024 * 1024 }) - * .on('progress', (state) => { - * console.log( state.percentage.toFixed(0) + '%' ) - * }) - */ - constructor (options) { - super(options) - - this.start = 0 - this.interval = options.time || DEFAULT_TIME_MS - this.timer = null - this.meter = speedometer() - - this.delta = 0 - - this.state = { - delta: 0, - eta: 0, - length: options.length, - percentage: 0, - remaining: 0, - runtime: 0, - speed: 0, - totalSpeed: 0, - transferred: 0 - } - - this.clear = () => { - clearInterval(this.timer) - } - - this.update = () => { - this.state.delta = this.delta - this.state.transferred += this.delta - this.state.percentage = this.state.transferred / this.state.length * PERCENT - this.state.remaining = this.state.length - this.state.transferred - this.state.runtime = Date.now() - this.start - this.state.speed = this.meter(this.state.delta) - - // NOTE: We need to guard against this becoming Infinity, - // because that value isn't transmitted properly over IPC and becomes `null` - this.state.eta = this.state.speed ? this.state.remaining / this.state.speed : 0 - this.delta = 0 - this.emit('progress', this.state) - } - - this.once('end', this.clear) - this.once('end', this.update) - this.once('error', this.clear) - - this.timer = setInterval(this.update, this.interval) - } - - /** - * @summary Transform function - * @private - * @param {Buffer} chunk - chunk - * @param {String} _ - encoding - * @param {Function} next - callback - * @example - * progressStream.write(buffer) - */ - _transform (chunk, _, next) { - this.start = this.start || Date.now() - this.delta += chunk.length - next(null, chunk) - } - - /** - * @summary Destroy handler - * @param {Error} [error] - error - * @param {Function} done - callback - * @example - * progressStream.destroy() - */ - _destroy (error, done) { - this.clear() - done(error) - } -} - -module.exports = ProgressStream diff --git a/lib/shared/drive-constraints.js b/lib/shared/drive-constraints.js index 21bfec08..f4924628 100644 --- a/lib/shared/drive-constraints.js +++ b/lib/shared/drive-constraints.js @@ -104,13 +104,9 @@ exports.isSystemDrive = (drive) => { * ] * }, { * path: '/Volumes/Untitled/image.img', - * size: { - * original: 1000000000, - * final: { - * estimation: false, - * value: 1000000000 - * } - * } + * size: 1000000000, + * compressedSize: 1000000000, + * isSizeEstimated: false, * })) { * console.log('This drive is a source drive!'); * } @@ -144,13 +140,9 @@ exports.isSourceDrive = (drive, image) => { * size: 1000000000 * }, { * path: 'rpi.img', - * size: { - * original: 1000000000, - * final: { - * estimation: false, - * value: 1000000000 - * } - * }, + * size: 1000000000, + * compressedSize: 1000000000, + * isSizeEstimated: false, * })) { * console.log('We can flash the image to this drive!'); * } @@ -158,12 +150,12 @@ exports.isSourceDrive = (drive, image) => { exports.isDriveLargeEnough = (drive, image) => { const driveSize = _.get(drive, [ 'size' ], UNKNOWN_SIZE) - if (_.get(image, [ 'size', 'final', 'estimation' ])) { + if (_.get(image, [ 'isSizeEstimated' ])) { // If the drive size is smaller than the original image size, and // the final image size is just an estimation, then we stop right // here, based on the assumption that the final size will never // be less than the original size. - if (driveSize < _.get(image, [ 'size', 'original' ], UNKNOWN_SIZE)) { + if (driveSize < _.get(image, [ 'compressedSize' ], UNKNOWN_SIZE)) { return false } @@ -174,11 +166,7 @@ exports.isDriveLargeEnough = (drive, image) => { return true } - return driveSize >= _.get(image, [ - 'size', - 'final', - 'value' - ], UNKNOWN_SIZE) + return driveSize >= _.get(image, [ 'size' ], UNKNOWN_SIZE) } /** @@ -220,13 +208,9 @@ exports.isDriveDisabled = (drive) => { * isReadOnly: false * }, { * path: 'rpi.img', - * size: { - * original: 1000000000, - * final: { - * estimation: false, - * value: 1000000000 - * } - * }, + * size: 1000000000, + * compressedSize: 1000000000, + * isSizeEstimated: false, * recommendedDriveSize: 2000000000 * })) { * console.log('This drive is valid!'); @@ -260,13 +244,9 @@ exports.isDriveValid = (drive, image) => { * * const image = { * path: 'rpi.img', - * size: { - * original: 2000000000, - * final: { - * estimation: false, - * value: 2000000000 - * } - * }, + * size: 2000000000, + * compressedSize: 2000000000, + * isSizeEstimated: false, * recommendedDriveSize: 4000000000 * }); * @@ -337,13 +317,9 @@ exports.COMPATIBILITY_STATUS_TYPES = { * * const image = { * path: '/path/to/rpi.img', - * size: { - * original: 2000000000, - * final: { - * estimation: false, - * value: 2000000000 - * } - * }, + * size: 2000000000, + * compressedSize: 2000000000, + * isSizeEstimated: false, * recommendedDriveSize: 4000000000 * }); * @@ -372,7 +348,7 @@ exports.getDriveImageCompatibilityStatuses = (drive, image) => { message: messages.compatibility.locked() }) } else if (!_.isNil(drive) && !_.isNil(drive.size) && !exports.isDriveLargeEnough(drive, image)) { - const imageSize = _.get(image, [ 'size', 'final', 'estimation' ]) ? image.size.original : image.size.final.value + const imageSize = image.isSizeEstimated ? image.compressedSize : image.size const relativeBytes = imageSize - drive.size statusList.push({ type: exports.COMPATIBILITY_STATUS_TYPES.ERROR, @@ -433,13 +409,9 @@ exports.getDriveImageCompatibilityStatuses = (drive, image) => { * * const image = { * path: '/path/to/rpi.img', - * size: { - * original: 2000000000, - * final: { - * estimation: false, - * value: 2000000000 - * } - * }, + * size: 2000000000, + * compressedSize: 2000000000, + * isSizeEstimated: false, * recommendedDriveSize: 4000000000 * }) * diff --git a/lib/shared/messages.js b/lib/shared/messages.js index a62a5464..2112777d 100644 --- a/lib/shared/messages.js +++ b/lib/shared/messages.js @@ -182,8 +182,8 @@ module.exports = { ].join(' ') }, - invalidImage: (image) => { - return `${image.path} is not a supported image type.` + invalidImage: (imagePath) => { + return `${imagePath} is not a supported image type.` }, openImage: (imageBasename, errorMessage) => { diff --git a/lib/shared/permissions.js b/lib/shared/permissions.js index 8799e985..c31533aa 100644 --- a/lib/shared/permissions.js +++ b/lib/shared/permissions.js @@ -219,6 +219,7 @@ exports.elevateCommand = (command, options) => { // for now, we should make sure we double check if the error messages // have changed every time we upgrade `sudo-prompt`. }).catch((error) => { + console.log('error', error.cause) return _.includes(error.message, 'is not in the sudoers file') }, () => { throw errors.createUserError({ diff --git a/lib/shared/supported-formats.js b/lib/shared/supported-formats.js index 32424cb6..3d26dfa8 100644 --- a/lib/shared/supported-formats.js +++ b/lib/shared/supported-formats.js @@ -16,29 +16,12 @@ 'use strict' +const sdk = require('etcher-sdk') const _ = require('lodash') +const mime = require('mime-types') const path = require('path') -const imageStream = require('../sdk/image-stream') -const fileExtensions = require('./file-extensions') -/** - * @summary Build an extension list getter from a type - * @function - * @private - * - * @param {String} type - file type - * @returns {Function} extension list getter - * - * @example - * const extensions = getExtensionsFromTypeGetter('archive')(); - */ -const getExtensionsFromTypeGetter = (type) => { - return () => { - return _.map(_.filter(imageStream.supportedFileTypes, { - type - }), 'extension') - } -} +const fileExtensions = require('./file-extensions') /** * @summary Get compressed extensions @@ -52,7 +35,18 @@ const getExtensionsFromTypeGetter = (type) => { * console.log('We support the ' + extension + ' compressed file format'); * }); */ -exports.getCompressedExtensions = getExtensionsFromTypeGetter('compressed') +exports.getCompressedExtensions = () => { + const result = [] + for (const [ mimetype, cls ] of sdk.sourceDestination.SourceDestination.mimetypes.entries()) { + if (cls.prototype instanceof sdk.sourceDestination.CompressedSource) { + const extension = mime.extension(mimetype) + if (extension) { + result.push(extension) + } + } + } + return result +} /** * @summary Get non compressed extensions @@ -66,7 +60,9 @@ exports.getCompressedExtensions = getExtensionsFromTypeGetter('compressed') * console.log('We support the ' + extension + ' file format'); * }); */ -exports.getNonCompressedExtensions = getExtensionsFromTypeGetter('image') +exports.getNonCompressedExtensions = () => { + return sdk.sourceDestination.SourceDestination.imageExtensions +} /** * @summary Get archive extensions @@ -80,7 +76,9 @@ exports.getNonCompressedExtensions = getExtensionsFromTypeGetter('image') * console.log('We support the ' + extension + ' file format'); * }); */ -exports.getArchiveExtensions = getExtensionsFromTypeGetter('archive') +exports.getArchiveExtensions = () => { + return [ 'zip', 'etch' ] +} /** * @summary Get all supported extensions @@ -95,7 +93,7 @@ exports.getArchiveExtensions = getExtensionsFromTypeGetter('archive') * }); */ exports.getAllExtensions = () => { - return _.map(imageStream.supportedFileTypes, 'extension') + return [ ...exports.getArchiveExtensions(), ...exports.getNonCompressedExtensions(), ...exports.getCompressedExtensions() ] } /** diff --git a/lib/shared/units.js b/lib/shared/units.js index 16b00293..6e4f5741 100644 --- a/lib/shared/units.js +++ b/lib/shared/units.js @@ -29,7 +29,7 @@ const prettyBytes = require('pretty-bytes') * @description * 1 MB = 1e+6 B */ -const MEGABYTE_TO_BYTE_RATIO = 1e+6 +const MEGABYTE_TO_BYTE_RATIO = 1000000 /** * @summary Milliseconds in a day diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e3719ca4..937823f7 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -2,6 +2,426 @@ "name": "balena-etcher", "version": "1.4.9", "dependencies": { + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "dev": true + }, + "@babel/core": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.2.0.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "dev": true + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.2.tgz", + "dev": true, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "dev": true + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz", + "dev": true + }, + "@babel/helper-builder-react-jsx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.0.0.tgz", + "dev": true + }, + "@babel/helper-call-delegate": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz", + "dev": true + }, + "@babel/helper-define-map": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz", + "dev": true + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "dev": true + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "dev": true + }, + "@babel/helper-hoist-variables": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz", + "dev": true + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz", + "dev": true + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "dev": true + }, + "@babel/helper-module-transforms": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.2.2.tgz", + "dev": true + }, + "@babel/helper-optimise-call-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz", + "dev": true + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0.tgz", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "dev": true + }, + "@babel/helper-replace-supers": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "dev": true + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "dev": true + }, + "@babel/helpers": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.2.0.tgz", + "dev": true + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "dev": true, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-proposal-function-bind": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-function-bind/-/plugin-proposal-function-bind-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-function-bind": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-jsx": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-classes": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.2.tgz", + "dev": true, + "dependencies": { + "globals": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-destructuring": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-for-of": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-function-name": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-new-target": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz", + "dev": true + }, + "@babel/plugin-transform-object-super": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-parameters": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-regenerator": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz", + "dev": true + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-spread": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz", + "dev": true + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-template-literals": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", + "dev": true + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz", + "dev": true + }, + "@babel/preset-env": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.2.0.tgz", + "dev": true, + "dependencies": { + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "dev": true + } + } + }, + "@babel/preset-react": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz", + "dev": true + }, "@babel/runtime": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", @@ -12,6 +432,45 @@ } } }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "dev": true + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "dev": true + }, + "globals": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.2.2.tgz", + "dev": true, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "dev": true + } + } + }, "@balena/grid-styled": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@balena/grid-styled/-/grid-styled-3.1.1.tgz", @@ -31,12 +490,12 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free-webfonts/-/fontawesome-free-webfonts-1.0.9.tgz" }, "@types/angular": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.51.tgz" + "version": "1.6.53", + "resolved": "https://registry.npmjs.org/@types/angular/-/angular-1.6.53.tgz" }, "@types/chart.js": { - "version": "2.7.41", - "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.41.tgz" + "version": "2.7.42", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.42.tgz" }, "@types/color": { "version": "2.0.1", @@ -75,12 +534,12 @@ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.3.0.tgz" }, "@types/node": { - "version": "10.12.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.12.tgz" + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz" }, "@types/prop-types": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.7.tgz" + "version": "15.5.8", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz" }, "@types/react": { "version": "16.3.14", @@ -114,6 +573,106 @@ "version": "3.4.4", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz" }, + "@webassemblyjs/ast": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-fsm": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/ieee754": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/leb128": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/utf8": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wasm-gen": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wasm-opt": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wasm-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wast-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", + "dev": true + }, + "@webassemblyjs/wast-printer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "dev": true + }, "7zip-bin": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-2.4.1.tgz", @@ -129,16 +688,9 @@ "dev": true }, "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "dev": true, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "dev": true - } - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "dev": true }, "acorn-jsx": { "version": "3.0.1", @@ -163,18 +715,18 @@ } }, "ajv": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz" + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "dev": true }, "ajv-keywords": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz" }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "dev": true - }, "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", @@ -254,182 +806,10 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" - }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "dev": true, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "dev": true, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "dev": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "dev": true - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "dev": true, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "dev": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "dev": true, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "dev": true - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "dev": true - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "dev": true - } - } + "dev": true }, "app-package-builder": { "version": "1.3.1", @@ -437,8 +817,8 @@ "dev": true }, "apple-data-compression": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/apple-data-compression/-/apple-data-compression-0.2.0.tgz" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/apple-data-compression/-/apple-data-compression-0.4.1.tgz" }, "aproba": { "version": "1.2.0", @@ -475,27 +855,10 @@ } }, "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "dev": true }, - "arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - }, - "make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "dev": true - } - } - }, "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", @@ -515,23 +878,6 @@ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", "dev": true }, - "array-slice": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", - "dev": true - }, - "array-sort": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-0.1.4.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "dev": true - } - } - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -543,31 +889,14 @@ "dev": true }, "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "dev": true }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" }, - "asar": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/asar/-/asar-0.14.2.tgz", - "dev": true, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "dev": true - }, - "tmp": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.28.tgz", - "dev": true - } - } - }, "asar-integrity": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asar-integrity/-/asar-integrity-0.2.3.tgz", @@ -620,8 +949,7 @@ }, "async": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz" }, "async-each": { "version": "1.0.1", @@ -647,11 +975,6 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "dev": true }, - "autolinker": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.15.3.tgz", - "dev": true - }, "aws-sign2": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" @@ -660,6 +983,10 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz" }, + "axios": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz" + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -671,8 +998,8 @@ } }, "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", "dependencies": { "debug": { "version": "2.6.9", @@ -684,418 +1011,36 @@ "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz" }, - "babel-helper-bindify-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", - "dev": true - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "dev": true - }, - "babel-helper-builder-react-jsx": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", - "dev": true - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "dev": true - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "dev": true - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "dev": true - }, - "babel-helper-explode-class": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", - "dev": true - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "dev": true - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "dev": true - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "dev": true - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "dev": true - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "dev": true - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "dev": true - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "dev": true - }, "babel-helpers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz" }, "babel-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.4.tgz", "dev": true }, "babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz" }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "dev": true - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-async-generators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-class-constructor-call": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", - "dev": true - }, - "babel-plugin-syntax-class-properties": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-decorators": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-do-expressions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-dynamic-import": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-export-extensions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-flow": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", - "dev": true - }, - "babel-plugin-syntax-function-bind": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-jsx": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "dev": true - }, - "babel-plugin-syntax-object-rest-spread": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-async-generator-functions": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-class-constructor-call": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-class-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-decorators": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-do-expressions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "dev": true - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-export-extensions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-flow-strip-types": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-function-bind": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-object-rest-spread": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", - "dev": true - }, - "babel-plugin-transform-react-display-name": { - "version": "6.25.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", - "dev": true - }, - "babel-plugin-transform-react-jsx": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", - "dev": true - }, - "babel-plugin-transform-react-jsx-self": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-react-jsx-source": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", - "dev": true - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "dev": true - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "dev": true - }, "babel-polyfill": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.23.0.tgz", "dependencies": { "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz" } } }, - "babel-preset-env": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", - "dev": true, - "dependencies": { - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "dev": true - } - } - }, - "babel-preset-flow": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", - "dev": true - }, - "babel-preset-react": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", - "dev": true - }, - "babel-preset-stage-0": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", - "dev": true - }, - "babel-preset-stage-1": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", - "dev": true - }, - "babel-preset-stage-2": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", - "dev": true - }, - "babel-preset-stage-3": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", - "dev": true - }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", "dependencies": { "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz" } } }, @@ -1104,8 +1049,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "dependencies": { "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz" }, "regenerator-runtime": { "version": "0.11.1", @@ -1142,34 +1087,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "dev": true - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "dev": true - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - } - } + "dev": true }, "base64-js": { "version": "1.3.0", @@ -1180,14 +1098,13 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" }, "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "dev": true }, "binary": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz" }, "binary-extensions": { "version": "1.12.0", @@ -1200,21 +1117,7 @@ }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "dependencies": { - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - } - } + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz" }, "block-stream": { "version": "0.0.9", @@ -1222,32 +1125,21 @@ "dev": true }, "blockmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blockmap/-/blockmap-2.0.2.tgz" + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/blockmap/-/blockmap-3.4.3.tgz" }, "bloodline": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bloodline/-/bloodline-1.0.1.tgz" }, "bluebird": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz" + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz" }, "bluebird-lst": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.6.tgz", - "dev": true, - "dependencies": { - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", - "dev": true - } - } - }, - "bluebird-retry": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/bluebird-retry/-/bluebird-retry-0.11.0.tgz" + "dev": true }, "bn.js": { "version": "4.11.8", @@ -1283,8 +1175,8 @@ "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "dev": true }, "has-flag": { @@ -1319,8 +1211,8 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" }, "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "dev": true }, "brorand": { @@ -1364,8 +1256,8 @@ "dev": true }, "browserslist": { - "version": "2.11.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.0.tgz", "dev": true }, "buffer": { @@ -1400,8 +1292,7 @@ }, "buffers": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" }, "builder-util": { "version": "3.0.13", @@ -1434,8 +1325,8 @@ "dev": true }, "source-map-support": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", "dev": true }, "supports-color": { @@ -1469,6 +1360,23 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "dev": true }, + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "dev": true, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "dev": true + } + } + }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1498,8 +1406,8 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz" }, "caniuse-lite": { - "version": "1.0.30000918", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000918.tgz", + "version": "1.0.30000929", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000929.tgz", "dev": true }, "capture-stack-trace": { @@ -1511,18 +1419,6 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "dev": true, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "dev": true - } - } - }, "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", @@ -1555,8 +1451,7 @@ }, "chainsaw": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz" }, "chalk": { "version": "1.1.3", @@ -1600,56 +1495,17 @@ "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "dev": true, - "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "dev": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "dev": true, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "dev": true - } - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "dev": true - } - } + "dev": true }, "chownr": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz" }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "dev": true + }, "chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -1677,7 +1533,14 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "dev": true + "dev": true, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "dev": true + } + } }, "clean-css": { "version": "4.2.1", @@ -1734,20 +1597,10 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" }, - "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "dev": true - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz" }, - "code-context": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/code-context/-/code-context-0.5.3.tgz", - "dev": true - }, "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz" @@ -1790,8 +1643,8 @@ "resolved": "https://registry.npmjs.org/command-join/-/command-join-2.0.0.tgz" }, "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz" + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" }, "comment-parser": { "version": "0.4.2", @@ -1869,6 +1722,11 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", "dev": true }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -1906,11 +1764,6 @@ "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", "dev": true }, - "create-frame": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/create-frame/-/create-frame-0.1.4.tgz", - "dev": true - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -1974,8 +1827,8 @@ "dev": true }, "csstype": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.8.tgz" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.0.tgz" }, "cuint": { "version": "0.2.2", @@ -1986,9 +1839,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" }, - "cwd": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.9.1.tgz", + "cyclic-32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cyclic-32/-/cyclic-32-1.1.0.tgz" + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", "dev": true }, "d": { @@ -2138,20 +1995,22 @@ } }, "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.0.tgz", + "dev": true, + "dependencies": { + "@types/node": { + "version": "8.10.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.39.tgz", + "dev": true + } + } }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", "dev": true }, - "date.js": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz", - "dev": true - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz" @@ -2169,33 +2028,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" }, - "decompress-zip": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/decompress-zip/-/decompress-zip-0.3.0.tgz", - "dev": true, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "dev": true - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "dev": true - } - } - }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -2224,18 +2056,6 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.0.1.tgz", "dev": true }, - "default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "dev": true - } - } - }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz" @@ -2243,20 +2063,30 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "dev": true + }, + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "dev": true, "dependencies": { - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "dev": true + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "dev": true + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "dev": true } } }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "dev": true - }, "degenerator": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", @@ -2385,6 +2215,11 @@ "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", "dev": true }, + "duplexify": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "dev": true + }, "easy-stack": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz" @@ -2425,6 +2260,11 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "dev": true }, + "base64-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", + "dev": true + }, "chalk": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", @@ -2465,6 +2305,11 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "dev": true }, + "plist": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", + "dev": true + }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", @@ -2497,6 +2342,11 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", "dev": true }, + "xmlbuilder": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", + "dev": true + }, "yargs": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-10.1.2.tgz", @@ -2558,11 +2408,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "dev": true }, - "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "dev": true - }, "semver": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", @@ -2579,6 +2424,11 @@ "resolved": "https://registry.npmjs.org/electron-mocha/-/electron-mocha-6.0.1.tgz", "dev": true, "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "dev": true + }, "fs-extra": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", @@ -2596,15 +2446,25 @@ "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.7.tgz", "dev": true, "dependencies": { - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "base64-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", "dev": true }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "dev": true + }, + "plist": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", + "dev": true + }, + "xmlbuilder": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", + "dev": true } } }, @@ -2636,8 +2496,8 @@ } }, "electron-to-chromium": { - "version": "1.3.90", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.90.tgz", + "version": "1.3.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz", "dev": true }, "electron-window": { @@ -2664,21 +2524,8 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz" }, "enhanced-resolve": { - "version": "4.0.0", - "from": "jviotti/enhanced-resolve#root-symlinks", - "resolved": "git://github.com/jviotti/enhanced-resolve.git#90fe6a799bc8472955e4c66bc12314b6874c8d02", - "dev": true, - "dependencies": { - "tapable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", - "dev": true - } - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", "dev": true }, "entities": { @@ -2706,8 +2553,8 @@ } }, "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", "dev": true }, "es-to-primitive": { @@ -2716,8 +2563,8 @@ "dev": true }, "es5-ext": { - "version": "0.10.46", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz" + "version": "0.10.47", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.47.tgz" }, "es6-iterator": { "version": "2.0.3", @@ -2808,8 +2655,8 @@ "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "dev": true }, "fast-deep-equal": { @@ -2818,8 +2665,8 @@ "dev": true }, "globals": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.9.0.tgz", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", "dev": true }, "has-flag": { @@ -3013,6 +2860,16 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" }, + "etcher-sdk": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/etcher-sdk/-/etcher-sdk-1.0.2.tgz", + "dependencies": { + "@types/node": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.2.tgz" + } + } + }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -3041,23 +2898,43 @@ "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz" }, "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "dev": true - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "dev": true + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "dev": true, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "dev": true + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "dev": true + } + } }, "expand-template": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-1.1.1.tgz" }, - "expand-tilde": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", - "dev": true + "ext2fs": { + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/ext2fs/-/ext2fs-1.0.25.tgz", + "dependencies": { + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz" + }, + "nan": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz" + }, + "prebuild-install": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.2.2.tgz" + } + } }, "extend": { "version": "3.0.2", @@ -3073,8 +2950,8 @@ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz" }, "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "dev": true }, "extract-zip": { @@ -3087,6 +2964,11 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "dev": true }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "dev": true + }, "yauzl": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", @@ -3120,124 +3002,67 @@ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", "dev": true }, + "fatfs": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/fatfs/-/fatfs-0.10.7.tgz" + }, "fbjs": { "version": "0.8.17", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz" }, "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" + }, + "fifolock": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fifolock/-/fifolock-1.0.0.tgz" + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "dev": true }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" }, - "file-contents": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/file-contents/-/file-contents-0.2.4.tgz", - "dev": true, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "dev": true - }, - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "dev": true - } - } + "file-disk": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/file-disk/-/file-disk-5.1.0.tgz" }, "file-entry-cache": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", "dev": true }, - "file-name": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/file-name/-/file-name-0.1.0.tgz", - "dev": true - }, - "file-stat": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/file-stat/-/file-stat-0.1.3.tgz", - "dev": true, - "dependencies": { - "lazy-cache": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "dev": true - } - } - }, "file-type": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-4.1.0.tgz" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz" }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "dev": true }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "dev": true - }, "filendir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/filendir/-/filendir-1.0.2.tgz", "dev": true }, "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "dev": true, "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "dev": true } } @@ -3247,23 +3072,19 @@ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", "dev": true }, - "find-file-up": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-0.1.3.tgz", - "dev": true - }, - "find-pkg": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-0.1.2.tgz", - "dev": true - }, "find-up": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz" }, "flat": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz" + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" + } + } }, "flat-cache": { "version": "1.3.4", @@ -3274,22 +3095,19 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/flexboxgrid/-/flexboxgrid-6.3.0.tgz" }, - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "flush-write-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", "dev": true }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "dev": true, - "dependencies": { - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "dev": true - } - } + "follow-redirects": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "dev": true }, "forever-agent": { "version": "0.6.1", @@ -3314,6 +3132,11 @@ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "dev": true }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "dev": true + }, "front-matter": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-2.1.2.tgz", @@ -3323,14 +3146,9 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" }, - "fs-exists-sync": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", - "dev": true - }, "fs-extra": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", "dev": true }, "fs-extra-p": { @@ -3354,6 +3172,11 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz" }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -3423,18 +3246,6 @@ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "dev": true }, - "get-object": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/get-object/-/get-object-0.2.0.tgz", - "dev": true, - "dependencies": { - "isobject": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-0.2.0.tgz", - "dev": true - } - } - }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" @@ -3444,13 +3255,23 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" }, "get-uri": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.3.tgz", "dev": true, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "dev": true + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", "dev": true } } @@ -3470,23 +3291,6 @@ } } }, - "git-config-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz", - "dev": true - }, - "git-repo-name": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/git-repo-name/-/git-repo-name-0.6.0.tgz", - "dev": true, - "dependencies": { - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "dev": true - } - } - }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz" @@ -3495,29 +3299,26 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "dev": true - }, "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "dev": true, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "dev": true + } + } }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", "dev": true }, - "global-modules": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", - "dev": true - }, - "global-prefix": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "global-modules-path": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.1.tgz", "dev": true }, "globals": { @@ -3552,8 +3353,8 @@ "dev": true }, "gpt": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gpt/-/gpt-1.0.0.tgz" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gpt/-/gpt-2.0.0.tgz" }, "graceful-fs": { "version": "4.1.15", @@ -3577,23 +3378,6 @@ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", "dev": true }, - "handlebars": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", - "dev": true, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "dev": true - } - } - }, - "handlebars-helpers": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.6.2.tgz", - "dev": true - }, "har-schema": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz" @@ -3633,18 +3417,13 @@ "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "dev": true + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "dev": true, "dependencies": { - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "dev": true - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -3664,11 +3443,6 @@ } } }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "dev": true - }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -3687,28 +3461,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz" }, - "helper-date": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/helper-date/-/helper-date-0.2.3.tgz", - "dev": true - }, - "helper-markdown": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-markdown/-/helper-markdown-0.2.2.tgz", - "dev": true, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "dev": true - } - } - }, - "helper-md": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/helper-md/-/helper-md-0.2.2.tgz", - "dev": true - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -3731,11 +3483,6 @@ "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.6.tgz", "dev": true }, - "homedir-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz" @@ -3743,14 +3490,7 @@ "html-angular-validate": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/html-angular-validate/-/html-angular-validate-0.2.3.tgz", - "dev": true, - "dependencies": { - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "dev": true - } - } + "dev": true }, "html-loader": { "version": "0.5.1", @@ -3769,18 +3509,13 @@ } } }, - "html-tag": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/html-tag/-/html-tag-0.2.1.tgz", - "dev": true - }, "htmlparser2": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", "dependencies": { "readable-stream": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz" } } }, @@ -3828,6 +3563,11 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz" }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "dev": true + }, "ignore": { "version": "3.3.10", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", @@ -3846,6 +3586,48 @@ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", "dev": true }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "dev": true, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "dev": true + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "dev": true + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "dev": true + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "dev": true + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3870,11 +3652,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz" }, - "index-of": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/index-of/-/index-of-0.2.0.tgz", - "dev": true - }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -3964,8 +3741,8 @@ "dev": true }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", "dev": true }, "invariant": { @@ -3982,19 +3759,21 @@ "dev": true }, "is": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", - "dev": true - }, - "is-absolute": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", "dev": true }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "dev": true + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true + } + } }, "is-arrayish": { "version": "0.3.2", @@ -4006,8 +3785,8 @@ "dev": true }, "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" }, "is-builtin-module": { "version": "1.0.0", @@ -4026,7 +3805,14 @@ "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "dev": true + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true + } + } }, "is-date-object": { "version": "1.0.1", @@ -4045,11 +3831,6 @@ } } }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "dev": true - }, "is-electron": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.0.tgz" @@ -4059,24 +3840,14 @@ "resolved": "https://registry.npmjs.org/is-electron-renderer/-/is-electron-renderer-2.0.1.tgz", "dev": true }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "dev": true - }, - "is-even": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-even/-/is-even-0.1.2.tgz", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "dev": true }, "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "dev": true }, "is-finite": { @@ -4088,8 +3859,8 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz" }, "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", "dev": true }, "is-installed-globally": { @@ -4112,28 +3883,11 @@ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", "dev": true }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "dev": true - }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "dev": true }, - "is-odd": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-0.1.2.tgz", - "dev": true, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "dev": true - } - } - }, "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", @@ -4147,16 +3901,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "dev": true - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz" @@ -4180,11 +3924,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" }, - "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "dev": true - }, "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", @@ -4208,23 +3947,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" }, - "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "dev": true - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz" }, - "is-valid-glob": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz", - "dev": true - }, "is-windows": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "dev": true }, "isarray": { @@ -4253,8 +3982,13 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" }, "js-base64": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz", + "dev": true + }, + "js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", "dev": true }, "js-message": { @@ -4270,8 +4004,8 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", "dev": true }, "jsbn": { @@ -4282,9 +4016,9 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "dev": true }, "json-schema": { @@ -4342,16 +4076,9 @@ "dev": true }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "dev": true - } - } + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "dev": true }, "klaw": { "version": "1.3.1", @@ -4368,11 +4095,6 @@ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", "dev": true }, - "lazy-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", - "dev": true - }, "lazy-val": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz", @@ -4392,24 +4114,26 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "dev": true }, - "list-item": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/list-item/-/list-item-1.1.1.tgz", - "dev": true - }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz" }, "loader-runner": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", "dev": true }, "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "dev": true + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "dev": true, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "dev": true + } + } }, "locate-path": { "version": "2.0.0", @@ -4517,21 +4241,11 @@ } } }, - "logging-helpers": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/logging-helpers/-/logging-helpers-0.4.0.tgz", - "dev": true - }, "lolex": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", "dev": true }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "dev": true - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" @@ -4552,499 +4266,21 @@ }, "lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "dependencies": { + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + } + } }, "lzma-native": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-1.5.2.tgz", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-4.0.3.tgz", "dependencies": { - "node-pre-gyp": { - "version": "0.6.29", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.29.tgz", - "dependencies": { - "abbrev": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz" - }, - "ansi-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz" - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" - }, - "aproba": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.0.4.tgz" - }, - "are-we-there-yet": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz" - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz" - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz" - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz" - }, - "aws4": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz" - }, - "balanced-match": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.1.tgz" - }, - "bl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "dependencies": { - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz" - } - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz" - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz" - }, - "brace-expansion": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.5.tgz" - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz" - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz" - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" - }, - "code-point-at": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.0.0.tgz" - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz" - }, - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz" - }, - "dashdash": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz" - }, - "deep-extend": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz" - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "optional": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - }, - "extend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz" - }, - "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" - }, - "form-data": { - "version": "1.0.0-rc4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc4.tgz" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - }, - "fstream": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz" - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz" - }, - "gauge": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.6.0.tgz" - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz" - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz" - }, - "getpass": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "glob": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz" - }, - "graceful-fs": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz" - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz" - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" - }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz" - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz" - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz" - }, - "inflight": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.5.tgz" - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" - }, - "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" - }, - "is-my-json-valid": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.13.1.tgz" - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" - }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "optional": true - }, - "jsbn": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz", - "optional": true - }, - "json-schema": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - }, - "jsonpointer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz" - }, - "jsprim": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz" - }, - "mime-db": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz" - }, - "mime-types": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz" - }, - "minimatch": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz" - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz" - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" - }, - "node-uuid": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.7.tgz" - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" - }, - "npmlog": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-3.1.2.tgz" - }, - "number-is-nan": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz" - }, - "object-assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz" - }, - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz" - }, - "path-is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" - }, - "qs": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.1.0.tgz" - }, - "rc": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" - } - } - }, - "readable-stream": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz" - }, - "request": { - "version": "2.72.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.72.0.tgz" - }, - "rimraf": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.2.tgz" - }, - "semver": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.2.0.tgz" - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - }, - "signal-exit": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.0.tgz" - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz" - }, - "sshpk": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.8.3.tgz", - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" - } - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" - }, - "string-width": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.1.tgz" - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" - }, - "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz" - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz" - }, - "tar-pack": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.1.4.tgz" - }, - "tough-cookie": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz" - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz" - }, - "tweetnacl": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz", - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - }, - "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz" - }, - "wide-align": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" - } - } + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz" } } }, @@ -5060,9 +4296,9 @@ } } }, - "make-iterator": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-0.2.1.tgz", + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", "dev": true }, "map-cache": { @@ -5079,45 +4315,17 @@ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "dev": true }, - "markdown": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz", - "dev": true, - "dependencies": { - "nopt": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz", - "dev": true - } - } - }, - "markdown-utils": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/markdown-utils/-/markdown-utils-0.7.3.tgz", - "dev": true - }, "marked": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz" }, - "math-random": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz", - "dev": true - }, "mbr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/mbr/-/mbr-1.1.2.tgz" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mbr/-/mbr-1.1.3.tgz" }, "md5": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - } - } + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz" }, "md5.js": { "version": "1.3.5", @@ -5152,9 +4360,41 @@ "dev": true }, "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "dev": true + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "dev": true, + "dependencies": { + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "dev": true + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "dev": true + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "dev": true + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "dev": true + } + } }, "miller-rabin": { "version": "4.0.1", @@ -5167,12 +4407,12 @@ "dev": true }, "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz" + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz" }, "mime-types": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz" + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz" }, "mimic-fn": { "version": "1.2.0", @@ -5202,28 +4442,34 @@ }, "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", - "dependencies": { - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" - } - } + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz" }, "minizlib": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz" }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "dev": true, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "dev": true + } + } + }, "mixin-deep": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", "dev": true, "dependencies": { - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "dev": true - }, "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", @@ -5236,8 +4482,8 @@ "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.7.0.tgz" }, "mixpanel-browser": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.24.0.tgz" + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.26.0.tgz" }, "mkdirp": { "version": "0.5.1", @@ -5249,16 +4495,6 @@ } } }, - "mkpath": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-0.1.0.tgz", - "dev": true - }, - "mksnapshot": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.3.4.tgz", - "dev": true - }, "mocha": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz", @@ -5297,8 +4533,8 @@ "dev": true }, "moment": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz" + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz" }, "moment-duration-format": { "version": "2.2.2", @@ -5314,6 +4550,11 @@ } } }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" @@ -5336,16 +4577,6 @@ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "dev": true, "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "dev": true - }, "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -5375,16 +4606,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true } } }, @@ -5426,6 +4647,11 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/ngcomponent/-/ngcomponent-4.1.0.tgz" }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "dev": true + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -5449,8 +4675,8 @@ } }, "node-abi": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.5.0.tgz", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.5.1.tgz", "dependencies": { "semver": { "version": "5.6.0", @@ -5595,6 +4821,33 @@ } } }, + "node-raspberrypi-usbboot": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/node-raspberrypi-usbboot/-/node-raspberrypi-usbboot-0.0.12.tgz", + "dependencies": { + "@types/node": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.2.tgz" + }, + "usb": { + "version": "1.3.5", + "from": "resin-io/node-usb#1.3.5", + "resolved": "git://github.com/resin-io/node-usb.git#1521d10bdfe09ef2ca1f259972dee27c6c35f821" + } + } + }, + "node-releases": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.3.tgz", + "dev": true, + "dependencies": { + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "dev": true + } + } + }, "node-sass": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz", @@ -5605,6 +4858,11 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", "dev": true }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "dev": true + }, "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", @@ -5632,10 +4890,6 @@ } } }, - "node-stream-zip": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.3.7.tgz" - }, "node.extend": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", @@ -5668,8 +4922,8 @@ "dev": true }, "npm-packlist": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz" }, "npm-run-path": { "version": "2.0.2", @@ -5706,11 +4960,23 @@ "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "dev": true + "dev": true, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true + } + } }, "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", "dev": true }, "object-visit": { @@ -5718,9 +4984,9 @@ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "dev": true }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", "dev": true }, "object.pick": { @@ -5728,11 +4994,6 @@ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "dev": true }, - "omit-empty": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/omit-empty/-/omit-empty-0.4.1.tgz", - "dev": true - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -5798,10 +5059,24 @@ "version": "0.1.5", "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" }, + "outdent": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz" + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" @@ -5842,8 +5117,13 @@ "dev": true }, "pako": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", "dev": true }, "param-case": { @@ -5856,16 +5136,6 @@ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "dev": true }, - "parse-author": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-1.0.0.tgz", - "dev": true - }, - "parse-code-context": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/parse-code-context/-/parse-code-context-0.1.3.tgz", - "dev": true - }, "parse-color": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", @@ -5878,29 +5148,13 @@ } } }, - "parse-git-config": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-1.1.1.tgz", - "dev": true - }, - "parse-github-url": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-0.3.2.tgz", - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "dev": true - }, "parse-json": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "dev": true + "partitioninfo": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/partitioninfo/-/partitioninfo-5.3.0.tgz" }, "pascalcase": { "version": "0.1.1", @@ -5984,10 +5238,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" }, - "pipage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pipage/-/pipage-1.0.2.tgz" - }, "pkg": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.3.0.tgz", @@ -6014,8 +5264,8 @@ "dev": true }, "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz", "dev": true }, "escodegen": { @@ -6104,8 +5354,8 @@ "dev": true }, "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz", "dev": true }, "expand-template": { @@ -6146,14 +5396,8 @@ } }, "plist": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz", - "dependencies": { - "base64-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz" - } - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz" }, "pluralize": { "version": "7.0.0", @@ -6183,11 +5427,6 @@ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", "dev": true }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "dev": true - }, "pretty-bytes": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz" @@ -6202,8 +5441,8 @@ "dev": true }, "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz" }, "progress": { "version": "2.0.3", @@ -6226,15 +5465,15 @@ } } }, - "project-name": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/project-name/-/project-name-0.2.6.tgz", - "dev": true - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "dev": true + }, "prop-types": { "version": "15.5.9", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.9.tgz" @@ -6289,6 +5528,11 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz" }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "dev": true + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" @@ -6324,23 +5568,6 @@ } } }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "dev": true, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - } - } - }, "randombytes": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", @@ -6410,16 +5637,16 @@ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-2.2.7.tgz" }, "react-jsonschema-form": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.0.6.tgz", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.2.0.tgz", "dependencies": { "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz" }, "core-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.0.tgz" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.2.tgz" }, "fast-deep-equal": { "version": "1.1.0", @@ -6471,187 +5698,13 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz" }, "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "dependencies": { - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz" - } - } + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz" }, "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "dev": true, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "dev": true, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "dev": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "dev": true - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "dev": true - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "dev": true, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "dev": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "dev": true, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "dev": true - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "dev": true - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "dev": true - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "dev": true, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "dev": true - } - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "dev": true - } - } + "dev": true }, "readline2": { "version": "1.0.1", @@ -6698,11 +5751,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz" }, - "reduce-object": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/reduce-object/-/reduce-object-0.1.3.tgz", - "dev": true - }, "redux": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/redux/-/redux-3.5.2.tgz" @@ -6712,18 +5760,18 @@ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", "dev": true }, + "regenerate-unicode-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz", + "dev": true + }, "regenerator-runtime": { "version": "0.10.5", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz" }, "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.3.tgz", "dev": true }, "regex-not": { @@ -6744,12 +5792,12 @@ } }, "regex-parser": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.9.tgz" + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.10.tgz" }, "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.4.0.tgz", "dev": true }, "registry-auth-token": { @@ -6763,13 +5811,13 @@ "dev": true }, "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", "dev": true }, "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", "dev": true, "dependencies": { "jsesc": { @@ -6784,40 +5832,6 @@ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "dev": true }, - "relative": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/relative/-/relative-3.0.2.tgz", - "dev": true, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "dev": true - } - } - }, - "remarkable": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.1.tgz", - "dev": true, - "dependencies": { - "argparse": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", - "dev": true - }, - "underscore.string": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", - "dev": true - } - } - }, - "remote-origin-url": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/remote-origin-url/-/remote-origin-url-0.5.3.tgz", - "dev": true - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -6855,15 +5869,9 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz" }, - "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "dev": true - }, - "repo-utils": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/repo-utils/-/repo-utils-0.3.7.tgz", - "dev": true + "replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz" }, "request": { "version": "2.81.0", @@ -6944,13 +5952,9 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" }, - "bluebird": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz" - }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz" + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" }, "cli-cursor": { "version": "1.0.2", @@ -7024,6 +6028,10 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/resin-device-status/-/resin-device-status-1.1.1.tgz" }, + "resin-image-fs": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/resin-image-fs/-/resin-image-fs-5.0.6.tgz" + }, "resin-semver": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/resin-semver/-/resin-semver-1.4.0.tgz", @@ -7035,14 +6043,21 @@ } }, "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", "dev": true }, - "resolve-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", - "dev": true + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "dev": true, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "dev": true + } + } }, "resolve-from": { "version": "1.0.1", @@ -7068,14 +6083,9 @@ "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.1.9.tgz", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "dev": true - }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" }, "ripemd160": { "version": "2.0.2", @@ -7090,6 +6100,11 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz" }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "dev": true + }, "rw": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz" @@ -7295,6 +6310,11 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "dev": true + }, "scope-css": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/scope-css/-/scope-css-1.2.1.tgz" @@ -7313,13 +6333,7 @@ }, "seek-bzip": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", - "dependencies": { - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz" - } - } + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz" }, "semver": { "version": "5.1.1", @@ -7330,15 +6344,15 @@ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", "dev": true }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" }, - "set-getter": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", - "dev": true - }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -7503,45 +6517,30 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "dev": true + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "dev": true } } }, "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "dev": true, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "dev": true - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "dev": true - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true - } - } + "dev": true }, "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "dev": true + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true + } + } }, "sntp": { "version": "1.0.9", @@ -7600,8 +6599,8 @@ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz" }, "spdx-license-ids": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz" }, "spectron": { "version": "3.7.3", @@ -7609,8 +6608,8 @@ "dev": true }, "speedometer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.0.0.tgz" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.1.0.tgz" }, "split": { "version": "1.0.1", @@ -7639,8 +6638,8 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz" }, "sshpk": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.0.tgz", "dependencies": { "assert-plus": { "version": "1.0.0", @@ -7648,6 +6647,11 @@ } } }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "dev": true + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz" @@ -7660,7 +6664,14 @@ "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "dev": true + "dev": true, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "dev": true + } + } }, "statuses": { "version": "1.5.0", @@ -7677,36 +6688,29 @@ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", "dev": true }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "dev": true + }, "stream-http": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "dev": true, - "dependencies": { - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "dev": true - } - } + "dev": true }, "stream-meter": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", "dev": true }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "dev": true + }, "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" }, "string-width": { "version": "1.0.2", @@ -7745,10 +6749,9 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" }, - "striptags": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/striptags/-/striptags-2.2.1.tgz", - "dev": true + "struct-fu": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/struct-fu/-/struct-fu-1.2.1.tgz" }, "styled-components": { "version": "3.2.3", @@ -7809,25 +6812,10 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "dev": true }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "dev": true - }, "qs": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.6.0.tgz", "dev": true - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "dev": true } } }, @@ -7860,8 +6848,8 @@ "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "dev": true }, "has-flag": { @@ -7896,19 +6884,13 @@ "resolved": "https://registry.npmjs.org/tag-hoc/-/tag-hoc-1.0.0.tgz" }, "tapable": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", "dev": true }, "tar": { "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", - "dependencies": { - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" - } - } + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz" }, "tar-fs": { "version": "1.16.3", @@ -7934,6 +6916,85 @@ "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", "dev": true }, + "terser": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.14.1.tgz", + "dev": true, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "dev": true + }, + "source-map-support": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz", + "dev": true, + "dependencies": { + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "dev": true + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "dev": true + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "dev": true + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "dev": true + } + } + }, "text-encoding": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", @@ -7963,6 +7024,11 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "dev": true }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "dev": true + }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -8011,27 +7077,17 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz" }, - "to-file": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/to-file/-/to-file-0.2.0.tgz", - "dev": true, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "dev": true - } - } - }, - "to-gfm-code-block": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/to-gfm-code-block/-/to-gfm-code-block-0.1.1.tgz", - "dev": true - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "dev": true + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true + } + } }, "to-regex": { "version": "3.0.2", @@ -8067,11 +7123,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "dev": true } } }, @@ -8084,6 +7135,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "dev": true } } }, @@ -8091,18 +7147,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" }, - "touch": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/touch/-/touch-0.0.3.tgz", - "dev": true, - "dependencies": { - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "dev": true - } - } - }, "tough-cookie": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", @@ -8120,8 +7164,7 @@ }, "traverse": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "dev": true + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" }, "trim-newlines": { "version": "1.0.0", @@ -8141,6 +7184,11 @@ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "dev": true }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "dev": true + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -8164,6 +7212,10 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "dev": true }, + "typed-error": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/typed-error/-/typed-error-3.0.2.tgz" + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -8174,8 +7226,8 @@ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.19.tgz" }, "udif": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/udif/-/udif-0.13.0.tgz" + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/udif/-/udif-0.15.7.tgz" }, "uglify-js": { "version": "3.4.9", @@ -8194,49 +7246,6 @@ } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "dev": true, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "dev": true - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "dev": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "dev": true - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "dev": true - } - } - }, "uid2": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", @@ -8244,23 +7253,33 @@ }, "unbzip2-stream": { "version": "1.2.5", - "from": "balena-io-modules/unbzip2-stream#core-streams", + "from": "balena-io-modules/unbzip2-stream#942fc218013c14adab01cf693b0500cf6ac83193", "resolved": "git://github.com/balena-io-modules/unbzip2-stream.git#942fc218013c14adab01cf693b0500cf6ac83193" }, - "unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "dev": true - }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "dev": true - }, "underscore.string": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz" }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "dev": true + }, + "unicode-match-property-value-ecmascript": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", + "dev": true + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -8273,6 +7292,16 @@ } } }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "dev": true + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "dev": true + }, "unique-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", @@ -8309,6 +7338,11 @@ "dev": true } } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "dev": true } } }, @@ -8317,23 +7351,15 @@ "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", "dev": true }, + "unzip-stream": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.0.tgz" + }, "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", "dev": true }, - "update-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/update-json/-/update-json-1.0.0.tgz", - "dev": true, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "dev": true - } - } - }, "update-notifier": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", @@ -8345,8 +7371,8 @@ "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "dev": true }, "has-flag": { @@ -8392,11 +7418,6 @@ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "dev": true }, - "usb": { - "version": "1.3.5", - "from": "balena-io/node-usb#1.3.5", - "resolved": "git://github.com/balena-io/node-usb.git#1521d10bdfe09ef2ca1f259972dee27c6c35f821" - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -8421,14 +7442,20 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" }, - "util-extend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz" + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "dev": true }, "uuid": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" }, + "v8-compile-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "dev": true + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" @@ -8443,73 +7470,6 @@ } } }, - "versionist": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/versionist/-/versionist-2.8.1.tgz", - "dev": true, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "dev": true - }, - "debug": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.5.1.tgz", - "dev": true - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "dev": true - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "dev": true - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "dev": true - }, - "touch": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-1.0.0.tgz", - "dev": true - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "dev": true - }, - "yargs": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", - "dev": true - }, - "yargs-parser": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", - "dev": true - } - } - }, - "vinyl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", - "dev": true - }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -8518,7 +7478,14 @@ "w3cjs": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/w3cjs/-/w3cjs-0.4.0.tgz", - "dev": true + "dev": true, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "dev": true + } + } }, "watchpack": { "version": "1.6.0", @@ -8535,8 +7502,8 @@ "dev": true }, "webdriverio": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-4.14.1.tgz", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-4.14.2.tgz", "dev": true, "dependencies": { "ansi-escapes": { @@ -8565,8 +7532,8 @@ "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "dev": true, "dependencies": { "supports-color": { @@ -8691,118 +7658,150 @@ } }, "webpack": { - "version": "3.10.0", - "from": "jviotti/webpack#symlink-fix", - "resolved": "git://github.com/jviotti/webpack.git#5bcd163f6ac7056063694c181a91fd5772c75fa1", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.27.0.tgz", "dev": true, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", "dev": true - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "dev": true - }, + } + } + }, + "webpack-cli": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz", + "dev": true, + "dependencies": { "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "dev": true }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "dev": true }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "dev": true, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "dev": true - } - } + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "dev": true }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", "dev": true }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "dev": true }, "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "dev": true }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", "dev": true }, - "load-json-file": { + "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "dev": true }, - "path-type": { + "lcid": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", "dev": true }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "dev": true }, - "read-pkg-up": { + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "dev": true + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "dev": true + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "dev": true + }, + "p-try": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "dev": true }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "dev": true - } - } + "dev": true }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "dev": true }, "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "dev": true }, "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", "dev": true }, "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", "dev": true } } @@ -8876,16 +7875,16 @@ } } }, - "window-size": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", - "dev": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "dev": true }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "dev": true + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" @@ -8924,8 +7923,8 @@ } }, "xmlbuilder": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz" + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz" }, "xmldom": { "version": "0.1.27", @@ -8935,6 +7934,10 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz" }, + "xok": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xok/-/xok-1.0.0.tgz" + }, "xregexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", @@ -8945,11 +7948,12 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz" }, "xterm": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/xterm/-/xterm-3.9.1.tgz" + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/xterm/-/xterm-3.10.1.tgz" }, "xxhash": { "version": "0.2.4", + "from": "balena-io-modules/node-xxhash#70ac31da1a41c6f8c53d931b5802c6c93f7b6b83", "resolved": "git://github.com/balena-io-modules/node-xxhash.git#70ac31da1a41c6f8c53d931b5802c6c93f7b6b83" }, "y18n": { @@ -8957,8 +7961,8 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz" }, "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz" }, "yargs": { "version": "11.0.0", @@ -9005,8 +8009,8 @@ } }, "yauzl": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.6.0.tgz" + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" }, "zip-stream": { "version": "1.2.0", diff --git a/package.json b/package.json index b24395a5..d9e1c45a 100644 --- a/package.json +++ b/package.json @@ -53,9 +53,7 @@ "angular-ui-bootstrap": "2.5.0", "angular-ui-router": "0.4.2", "bindings": "1.3.0", - "blockmap": "2.0.2", - "bluebird": "3.4.1", - "bluebird-retry": "0.11.0", + "bluebird": "3.5.3", "bootstrap-sass": "3.3.6", "chalk": "1.1.3", "color": "2.0.1", @@ -63,27 +61,20 @@ "debug": "3.1.0", "drivelist": "6.4.6", "electron-is-running-in-asar": "1.0.0", - "file-type": "4.1.0", + "etcher-sdk": "^1.0.2", "flexboxgrid": "6.3.0", - "gpt": "1.0.0", "immutable": "3.8.1", "inactivity-timer": "1.0.0", "lodash": "4.17.10", - "lzma-native": "1.5.2", - "mbr": "1.1.2", - "mime-types": "2.1.15", - "mountutils": "1.3.16", + "mime-types": "2.1.18", "nan": "2.9.2", "node-ipc": "9.1.1", - "node-stream-zip": "1.3.7", "path-is-inside": "1.0.2", - "pipage": "1.0.2", "pretty-bytes": "1.0.4", "prop-types": "15.5.9", "react": "16.3.2", "react-dom": "16.3.2", "react2angular": "4.0.2", - "readable-stream": "2.3.3", "redux": "3.5.2", "rendition": "4.41.1", "request": "2.81.0", @@ -92,30 +83,23 @@ "resin-corvus": "2.0.0", "roboto-fontface": "0.9.0", "semver": "5.1.1", - "speedometer": "1.0.0", "styled-components": "3.2.3", - "styled-system": "3.1.11", + "styled-system": "^3.1.11", "sudo-prompt": "8.2.3", - "udif": "0.13.0", - "unbzip2-stream": "github:balena-io-modules/unbzip2-stream#core-streams", - "usb": "github:balena-io/node-usb#1.3.5", "uuid": "3.0.1", "xml2js": "0.4.17", - "xxhash": "git://github.com/balena-io-modules/node-xxhash.git#70ac31da1a41c6f8c53d931b5802c6c93f7b6b83", - "yargs": "11.0.0", - "yauzl": "2.6.0" + "yargs": "11.0.0" }, "optionalDependencies": { "winusb-driver-generator": "1.2.4" }, "devDependencies": { + "@babel/core": "7.2.0", + "@babel/plugin-proposal-function-bind": "7.2.0", + "@babel/preset-env": "7.2.0", + "@babel/preset-react": "7.0.0", "angular-mocks": "1.6.3", - "asar": "0.14.2", - "babel-core": "6.26.0", - "babel-loader": "7.1.2", - "babel-preset-env": "1.6.1", - "babel-preset-react": "6.24.1", - "babel-preset-stage-0": "6.24.1", + "babel-loader": "8.0.4", "electron": "1.7.13", "electron-builder": "19.40.0", "electron-mocha": "6.0.1", @@ -139,8 +123,8 @@ "sass-lint": "1.12.1", "simple-progress-webpack-plugin": "1.1.2", "spectron": "3.7.3", - "versionist": "2.8.1", - "webpack": "github:jviotti/webpack#symlink-fix", + "webpack": "4.27.0", + "webpack-cli": "3.1.2", "webpack-node-externals": "1.7.2" } } diff --git a/patches/allow-electron-forks-of-modules-that-use-pre-gyp.patch b/patches/allow-electron-forks-of-modules-that-use-pre-gyp.patch new file mode 100644 index 00000000..5ab099e2 --- /dev/null +++ b/patches/allow-electron-forks-of-modules-that-use-pre-gyp.patch @@ -0,0 +1,28 @@ +--- a/node_modules/node-pre-gyp/lib/util/versioning.js ++++ b/node_modules/node-pre-gyp/lib/util/versioning.js +@@ -80,7 +80,14 @@ function get_runtime_abi(runtime, target_version) { + if (runtime === 'node-webkit') { + return get_node_webkit_abi(runtime, target_version || process.versions['node-webkit']); + } else if (runtime === 'electron') { +- return get_electron_abi(runtime, target_version || process.versions.electron); ++ var electron_version = target_version || process.versions.electron; ++ if (!electron_version) { ++ // TODO PR something to electron to pass in the version number for forks ++ // https://github.com/electron/electron/issues/9058 ++ try { electron_version = require('electron/package.json').version; } ++ catch (_) { electron_version = '1.7.13'; } ++ } ++ return get_electron_abi(runtime, electron_version); + } else { + if (runtime != 'node') { + throw new Error("Unknown Runtime: '" + runtime + "'"); +@@ -263,7 +270,8 @@ function get_process_runtime(versions) { + var runtime = 'node'; + if (versions['node-webkit']) { + runtime = 'node-webkit'; +- } else if (versions.electron) { ++ } else if (versions.electron || process.env.ELECTRON_RUN_AS_NODE) { ++ // Running in electron or a childProcess.fork of electron + runtime = 'electron'; + } + return runtime; diff --git a/patches/lzma-native-index-static-addon-require.patch b/patches/lzma-native-index-static-addon-require.patch deleted file mode 100644 index 6e50640b..00000000 --- a/patches/lzma-native-index-static-addon-require.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- b/node_modules/lzma-native/index.js 2018-01-23 14:37:50.000000000 -0400 -+++ a/node_modules/lzma-native/index.js 2018-01-23 14:37:00.000000000 -0400 -@@ -7,11 +7,8 @@ var extend = require('util-extend'); - var assert = require('assert'); - var fs = require('fs'); - --// node-pre-gyp magic --var nodePreGyp = require('node-pre-gyp'); - var path = require('path'); --var binding_path = nodePreGyp.find(path.resolve(path.join(__dirname,'./package.json'))); --var native = require(binding_path); -+var native = require(path.join(__dirname, 'binding', 'lzma_native.node')); - - extend(exports, native); - diff --git a/tests/gui/models/available-drives.spec.js b/tests/gui/models/available-drives.spec.js index 06f78bcb..5b6af4fc 100644 --- a/tests/gui/models/available-drives.spec.js +++ b/tests/gui/models/available-drives.spec.js @@ -154,13 +154,8 @@ describe('Model: availableDrives', function () { selectionState.selectImage({ path: this.imagePath, extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, + isSizeEstimated: false, recommendedDriveSize: 2000000000 }) }) diff --git a/tests/gui/models/flash-state.spec.js b/tests/gui/models/flash-state.spec.js index 6973cdb4..db35c667 100644 --- a/tests/gui/models/flash-state.spec.js +++ b/tests/gui/models/flash-state.spec.js @@ -48,8 +48,8 @@ describe('Model: flashState', function () { successful: 0, failed: 0, percentage: 0, - speed: 0, - totalSpeed: 0 + speed: null, + totalSpeed: null }) }) @@ -126,22 +126,6 @@ describe('Model: flashState', function () { }).to.not.throw('Missing flash fields: percentage') }) - it('should throw if percentage is missing', function () { - flashState.setFlashingFlag() - m.chai.expect(function () { - flashState.setProgressState({ - flashing: 2, - verifying: 0, - successful: 0, - failed: 0, - type: 'write', - eta: 15, - speed: 100000000000, - totalSpeed: 200000000000 - }) - }).to.throw('Missing flash fields: percentage') - }) - it('should throw if percentage is not a number', function () { flashState.setFlashingFlag() m.chai.expect(function () { @@ -193,22 +177,6 @@ describe('Model: flashState', function () { }).to.throw('Invalid state percentage: -1') }) - it('should throw if eta is missing', function () { - flashState.setFlashingFlag() - m.chai.expect(function () { - flashState.setProgressState({ - flashing: 2, - verifying: 0, - successful: 0, - failed: 0, - type: 'write', - percentage: 50, - speed: 100000000000, - totalSpeed: 200000000000 - }) - }).to.throw('Missing flash fields: eta') - }) - it('should not throw if eta is equal to zero', function () { flashState.setFlashingFlag() m.chai.expect(function () { @@ -384,8 +352,8 @@ describe('Model: flashState', function () { successful: 0, failed: 0, percentage: 0, - speed: 0, - totalSpeed: 0 + speed: null, + totalSpeed: null }) }) @@ -532,8 +500,8 @@ describe('Model: flashState', function () { successful: 0, failed: 0, percentage: 0, - speed: 0, - totalSpeed: 0 + speed: null, + totalSpeed: null }) }) diff --git a/tests/gui/models/selection-state.spec.js b/tests/gui/models/selection-state.spec.js index cf276589..d42c8f00 100644 --- a/tests/gui/models/selection-state.spec.js +++ b/tests/gui/models/selection-state.spec.js @@ -390,13 +390,7 @@ describe('Model: selectionState', function () { this.image = { path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, recommendedDriveSize: 1000000000, url: 'https://www.raspbian.org', supportUrl: 'https://www.raspbian.org/forums/', @@ -491,13 +485,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'bar.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) const imagePath = selectionState.getImagePath() @@ -527,13 +516,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) const imagePath = selectionState.getImagePath() @@ -547,13 +531,8 @@ describe('Model: selectionState', function () { path: 'foo.zip', extension: 'img', archiveExtension: 'zip', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) const imagePath = selectionState.getImagePath() @@ -565,13 +544,8 @@ describe('Model: selectionState', function () { path: 'foo.xz', extension: 'img', archiveExtension: 'xz', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) const imagePath = selectionState.getImagePath() @@ -583,13 +557,8 @@ describe('Model: selectionState', function () { path: 'something.linux-x86-64.gz', extension: 'img', archiveExtension: 'gz', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) const imagePath = selectionState.getImagePath() @@ -600,13 +569,8 @@ describe('Model: selectionState', function () { m.chai.expect(function () { selectionState.selectImage({ extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Missing image fields: path') }) @@ -616,13 +580,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 123, extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Invalid image path: 123') }) @@ -631,13 +590,8 @@ describe('Model: selectionState', function () { m.chai.expect(function () { selectionState.selectImage({ path: 'foo.img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Missing image fields: extension') }) @@ -647,13 +601,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 1, - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Invalid image extension: 1') }) @@ -663,13 +612,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'iso', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Missing image archive extension') }) @@ -680,15 +624,10 @@ describe('Model: selectionState', function () { path: 'foo.img', extension: 'iso', archiveExtension: 1, - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) - }).to.throw('Invalid image archive extension: 1') + }).to.throw('Missing image archive extension') }) it('should throw if the archive extension doesn\'t match the last path extension in a compressed image', function () { @@ -697,13 +636,8 @@ describe('Model: selectionState', function () { path: 'foo.img.xz', extension: 'img', archiveExtension: 'gz', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Image archive extension mismatch: gz and xz') }) @@ -713,13 +647,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.ifg', extension: 'ifg', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Invalid image extension: ifg') }) @@ -730,13 +659,8 @@ describe('Model: selectionState', function () { path: 'foo.ifg.gz', extension: 'ifg', archiveExtension: 'gz', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Invalid image extension: ifg') }) @@ -747,50 +671,22 @@ describe('Model: selectionState', function () { path: 'foo.img.ifg', extension: 'img', archiveExtension: 'ifg', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }).to.throw('Invalid image archive extension: ifg') }) - it('should throw if no size', function () { - m.chai.expect(function () { - selectionState.selectImage({ - path: 'foo.img', - extension: 'img' - }) - }).to.throw('Missing image fields: size') - }) - - it('should throw if size is not a plain object', function () { - m.chai.expect(function () { - selectionState.selectImage({ - path: 'foo.img', - extension: 'img', - size: 999999999 - }) - }).to.throw('Invalid image size: 999999999') - }) - it('should throw if the original size is not a number', function () { m.chai.expect(function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: '999999999', - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + compressedSize: '999999999', + isSizeEstimated: false }) - }).to.throw('Invalid original image size: 999999999') + }).to.throw('Invalid image compressed size: 999999999') }) it('should throw if the original size is a float number', function () { @@ -798,15 +694,11 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999.999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + compressedSize: 999999999.999, + isSizeEstimated: false }) - }).to.throw('Invalid original image size: 999999999.999') + }).to.throw('Invalid image compressed size: 999999999.999') }) it('should throw if the original size is negative', function () { @@ -814,15 +706,11 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: -1, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + compressedSize: -1, + isSizeEstimated: false }) - }).to.throw('Invalid original image size: -1') + }).to.throw('Invalid image compressed size: -1') }) it('should throw if the final size is not a number', function () { @@ -830,15 +718,10 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: '999999999' - } - } + size: '999999999', + isSizeEstimated: false }) - }).to.throw('Invalid final image size: 999999999') + }).to.throw('Invalid image size: 999999999') }) it('should throw if the final size is a float number', function () { @@ -846,15 +729,10 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999.999 - } - } + size: 999999999.999, + isSizeEstimated: false }) - }).to.throw('Invalid final image size: 999999999.999') + }).to.throw('Invalid image size: 999999999.999') }) it('should throw if the final size is negative', function () { @@ -862,31 +740,10 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: -1 - } - } + size: -1, + isSizeEstimated: false }) - }).to.throw('Invalid final image size: -1') - }) - - it('should throw if the final size estimation flag is not a boolean', function () { - m.chai.expect(function () { - selectionState.selectImage({ - path: 'foo.img', - extension: 'img', - size: { - original: 999999999, - final: { - estimation: 'false', - value: 999999999 - } - } - }) - }).to.throw('Invalid final image size estimation flag: false') + }).to.throw('Invalid image size: -1') }) it('should throw if url is defined but it\'s not a string', function () { @@ -894,13 +751,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, + isSizeEstimated: false, url: 1234 }) }).to.throw('Invalid image url: 1234') @@ -911,13 +763,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, + isSizeEstimated: false, name: 1234 }) }).to.throw('Invalid image name: 1234') @@ -928,13 +775,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, + isSizeEstimated: false, logo: 1234 }) }).to.throw('Invalid image logo: 1234') @@ -956,13 +798,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 1234567890, - final: { - estimation: false, - value: 1234567890 - } - } + size: 1234567890, + isSizeEstimated: false }) m.chai.expect(selectionState.hasDrive()).to.be.false @@ -985,13 +822,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - }, + size: 999999999, + isSizeEstimated: false, recommendedDriveSize: 1500000000 }) @@ -1028,13 +860,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: imagePath, extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) m.chai.expect(selectionState.hasDrive()).to.be.false @@ -1059,13 +886,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }) @@ -1177,13 +999,8 @@ describe('Model: selectionState', function () { selectionState.selectImage({ path: 'foo.img', extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) }) diff --git a/tests/gui/modules/drive-scanner.spec.js b/tests/gui/modules/drive-scanner.spec.js deleted file mode 100644 index 626a1d35..00000000 --- a/tests/gui/modules/drive-scanner.spec.js +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const os = require('os') -const drivelist = require('drivelist') -const driveScanner = require('../../../lib/gui/app/modules/drive-scanner') - -describe('Browser: driveScanner', function () { - describe('detected devices should be an array', function () { - it('should emit an empty array', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.be.an.instanceof(Array) - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - - describe('given only system available drives', function () { - beforeEach(function () { - this.drivelistStub = m.sinon.stub(drivelist, 'list') - this.drivelistStub.yields(null, [ - { - device: '/dev/sda', - description: 'WDC WD10JPVX-75J', - size: '931.5G', - mountpoints: [ - { - path: '/' - } - ], - isSystem: true - } - ]) - }) - - afterEach(function () { - this.drivelistStub.restore() - }) - - it('should emit an empty array', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.deep.equal([]) - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - - describe('given linux', function () { - beforeEach(function () { - this.osPlatformStub = m.sinon.stub(os, 'platform') - this.osPlatformStub.returns('linux') - }) - - afterEach(function () { - this.osPlatformStub.restore() - }) - - describe('given available drives', function () { - beforeEach(function () { - this.drivelistStub = m.sinon.stub(drivelist, 'list') - this.drivelistStub.yields(null, [ - { - device: '/dev/sda', - displayName: '/dev/sda', - description: 'WDC WD10JPVX-75J', - size: '931.5G', - mountpoints: [ - { - path: '/' - } - ], - isSystem: true, - isRemovable: false - }, - { - device: '/dev/sdb', - displayName: '/dev/sdb', - description: 'Foo', - size: 14000000000, - mountpoints: [ - { - path: '/mnt/foo' - } - ], - isSystem: false, - isRemovable: false - }, - { - device: '/dev/sdc', - displayName: '/dev/sdc', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: '/mnt/bar' - } - ], - isSystem: false, - isRemovable: false - } - ]) - }) - - afterEach(function () { - this.drivelistStub.restore() - }) - - it('should emit the non removable drives', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.deep.equal([ - { - device: '/dev/sdb', - displayName: '/dev/sdb', - description: 'Foo', - size: 14000000000, - mountpoints: [ - { - path: '/mnt/foo' - } - ], - adapter: 'blockdevice', - isSystem: false, - isRemovable: false - }, - { - device: '/dev/sdc', - displayName: '/dev/sdc', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: '/mnt/bar' - } - ], - adapter: 'blockdevice', - isSystem: false, - isRemovable: false - } - ]) - - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - }) - - describe('given windows', function () { - beforeEach(function () { - this.osPlatformStub = m.sinon.stub(os, 'platform') - this.osPlatformStub.returns('win32') - }) - - afterEach(function () { - this.osPlatformStub.restore() - }) - - describe('given available drives', function () { - beforeEach(function () { - this.drivelistStub = m.sinon.stub(drivelist, 'list') - this.drivelistStub.yields(null, [ - { - device: '\\\\.\\PHYSICALDRIVE1', - displayName: 'C:', - description: 'WDC WD10JPVX-75J', - size: '931.5G', - mountpoints: [ - { - path: 'C:' - } - ], - isSystem: true, - isRemovable: false - }, - { - device: '\\\\.\\PHYSICALDRIVE2', - displayName: '\\\\.\\PHYSICALDRIVE2', - description: 'Foo', - size: 14000000000, - mountpoints: [], - isSystem: false, - isRemovable: false - }, - { - device: '\\\\.\\PHYSICALDRIVE3', - displayName: 'F:', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: 'F:' - } - ], - isSystem: false, - isRemovable: false - } - ]) - }) - - afterEach(function () { - this.drivelistStub.restore() - }) - - it('should emit the non removable drives', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.deep.equal([ - { - device: '\\\\.\\PHYSICALDRIVE2', - displayName: '\\\\.\\PHYSICALDRIVE2', - description: 'Foo', - size: 14000000000, - mountpoints: [], - adapter: 'blockdevice', - isSystem: false, - isRemovable: false - }, - { - device: '\\\\.\\PHYSICALDRIVE3', - displayName: 'F:', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: 'F:' - } - ], - adapter: 'blockdevice', - isSystem: false, - isRemovable: false - } - ]) - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - - describe('given a drive with a single drive letters', function () { - beforeEach(function () { - this.drivelistStub = m.sinon.stub(drivelist, 'list') - this.drivelistStub.yields(null, [ - { - device: '\\\\.\\PHYSICALDRIVE3', - raw: '\\\\.\\PHYSICALDRIVE3', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: 'F:' - } - ], - isSystem: false, - isRemovable: true - } - ]) - }) - - afterEach(function () { - this.drivelistStub.restore() - }) - - it('should use the drive letter as the name', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.have.length(1) - m.chai.expect(drives[0].displayName).to.equal('F:') - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - - describe('given a drive with multiple drive letters', function () { - beforeEach(function () { - this.drivesListStub = m.sinon.stub(drivelist, 'list') - this.drivesListStub.yields(null, [ - { - device: '\\\\.\\PHYSICALDRIVE3', - raw: '\\\\.\\PHYSICALDRIVE3', - description: 'Bar', - size: 14000000000, - mountpoints: [ - { - path: 'F:' - }, - { - path: 'G:' - }, - { - path: 'H:' - } - ], - isSystem: false, - isRemovable: true - } - ]) - }) - - afterEach(function () { - this.drivesListStub.restore() - }) - - it('should join all the mountpoints in `name`', function (done) { - const spy = m.sinon.spy() - - driveScanner.once('devices', function (drives) { - let error = null - try { - m.chai.expect(drives).to.have.length(1) - m.chai.expect(drives[0].displayName).to.equal('F:, G:, H:') - m.chai.expect(spy).to.not.have.been.called - } catch (exception) { - error = exception - } - driveScanner.removeListener('error', spy) - driveScanner.stop() - done(error) - }) - - driveScanner.on('error', spy) - driveScanner.start() - }) - }) - }) - - describe('given an error when listing the drives', function () { - beforeEach(function () { - this.drivesListStub = m.sinon.stub(drivelist, 'list') - this.drivesListStub.yields(new Error('scan error')) - }) - - afterEach(function () { - this.drivesListStub.restore() - }) - - it('should emit the error', function (done) { - driveScanner.once('error', function (error) { - let assertionError = null - try { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal('scan error') - } catch (exception) { - assertionError = exception - } - driveScanner.stop() - done(assertionError) - }) - - driveScanner.start() - }) - }) -}) diff --git a/tests/gui/pages/main.spec.js b/tests/gui/pages/main.spec.js index 05e78305..adfec46d 100644 --- a/tests/gui/pages/main.spec.js +++ b/tests/gui/pages/main.spec.js @@ -70,13 +70,8 @@ describe('Browser: MainPage', function () { selectionState.selectImage({ path: 'rpi.img', extension: 'img', - size: { - original: 99999, - final: { - estimation: false, - value: 99999 - } - } + size: 99999, + isSizeEstimated: false }) m.chai.expect(controller.shouldDriveStepBeDisabled()).to.be.false @@ -103,13 +98,8 @@ describe('Browser: MainPage', function () { selectionState.selectImage({ path: 'rpi.img', extension: 'img', - size: { - original: 99999, - final: { - estimation: false, - value: 99999 - } - } + size: 99999, + isSizeEstimated: false }) m.chai.expect(controller.shouldFlashStepBeDisabled()).to.be.true @@ -157,13 +147,8 @@ describe('Browser: MainPage', function () { selectionState.selectImage({ path: 'rpi.img', extension: 'img', - size: { - original: 99999, - final: { - estimation: false, - value: 99999 - } - } + size: 99999, + isSizeEstimated: false }) m.chai.expect(controller.shouldFlashStepBeDisabled()).to.be.false @@ -197,13 +182,8 @@ describe('Browser: MainPage', function () { selectionState.selectImage({ path: path.join(__dirname, 'foo', 'bar.img'), extension: 'img', - size: { - original: 999999999, - final: { - estimation: false, - value: 999999999 - } - } + size: 999999999, + isSizeEstimated: false }) m.chai.expect(controller.getImageBasename()).to.equal('bar.img') diff --git a/tests/image-stream/archive-hooks/zip.spec.js b/tests/image-stream/archive-hooks/zip.spec.js deleted file mode 100644 index aab16827..00000000 --- a/tests/image-stream/archive-hooks/zip.spec.js +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const path = require('path') -const zipHooks = require('../../../lib/sdk/image-stream/archive-hooks/zip') -const utils = require('../../../lib/sdk/image-stream/utils') -const tester = require('../tester') -const ZIP_PATH = path.join(__dirname, '..', 'data', 'zip') - -describe('ImageStream: Archive hooks: ZIP', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getEntries()', function () { - describe('given an empty zip', function () { - beforeEach(function () { - this.zip = path.join(ZIP_PATH, 'zip-directory-empty.zip') - }) - - it('should become an empty array', function () { - return zipHooks.getEntries(this.zip).then((entries) => { - m.chai.expect(entries).to.deep.equal([]) - }) - }) - }) - - describe('given a zip with multiple files in it', function () { - beforeEach(function () { - this.zip = path.join(ZIP_PATH, 'zip-directory-multiple-images.zip') - }) - - it('should become all entries', function () { - return zipHooks.getEntries(this.zip).then((entries) => { - m.chai.expect(entries).to.deep.equal([ - { - name: 'multiple-images/edison-config.img', - size: 16777216 - }, - { - name: 'multiple-images/raspberrypi.img', - size: 33554432 - } - ]) - }) - }) - }) - - describe('given a zip with nested files in it', function () { - beforeEach(function () { - this.zip = path.join(ZIP_PATH, 'zip-directory-nested-misc.zip') - }) - - it('should become all entries', function () { - return zipHooks.getEntries(this.zip).then((entries) => { - m.chai.expect(entries).to.deep.equal([ - { - name: 'zip-directory-nested-misc/foo', - size: 4 - }, - { - name: 'zip-directory-nested-misc/hello/there/bar', - size: 4 - } - ]) - }) - }) - }) - }) - - describe('.extractFile()', function () { - beforeEach(function () { - this.zip = path.join(ZIP_PATH, 'zip-directory-nested-misc.zip') - }) - - it('should be able to extract a top-level file', function () { - const fileName = 'zip-directory-nested-misc/foo' - return zipHooks.getEntries(this.zip).then((entries) => { - return zipHooks.extractFile(this.zip, entries, fileName) - }).then(utils.extractStream).then((data) => { - m.chai.expect(data.toString()).to.equal('foo\n') - }) - }) - - it('should be able to extract a nested file', function () { - const fileName = 'zip-directory-nested-misc/hello/there/bar' - return zipHooks.getEntries(this.zip).then((entries) => { - return zipHooks.extractFile(this.zip, entries, fileName) - }).then(utils.extractStream).then((data) => { - m.chai.expect(data.toString()).to.equal('bar\n') - }) - }) - - it('should throw if the entry does not exist', function () { - const fileName = 'zip-directory-nested-misc/xxxxxxxxxxxxxxxx' - return zipHooks.getEntries(this.zip).then((entries) => { - return zipHooks.extractFile(this.zip, entries, fileName) - }).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal(`Invalid entry: ${fileName}`) - }) - }) - - it('should throw if the entry is a directory', function () { - const fileName = 'zip-directory-nested-misc/hello' - return zipHooks.getEntries(this.zip).then((entries) => { - return zipHooks.extractFile(this.zip, entries, fileName) - }).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal(`Invalid entry: ${fileName}`) - }) - }) - }) -}) diff --git a/tests/image-stream/bz2.spec.js b/tests/image-stream/bz2.spec.js deleted file mode 100644 index 1cff944d..00000000 --- a/tests/image-stream/bz2.spec.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const BZ2_PATH = path.join(DATA_PATH, 'bz2') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: BZ2', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('compression method', function () { - describe('bzip2 level 9', function () { - tester.extractFromFilePath( - path.join(BZ2_PATH, 'etcher-test-9.img.bz2'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - describe('bzip2 level 1', function () { - tester.extractFromFilePath( - path.join(BZ2_PATH, 'etcher-test.img.bz2'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(BZ2_PATH, 'etcher-test.img.bz2') - const expectedSize = fs.statSync(image).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - archiveExtension: 'bz2', - size: { - original: expectedSize, - final: { - estimation: true, - value: expectedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) -}) diff --git a/tests/image-stream/data/bz2/etcher-test-9.img.bz2 b/tests/image-stream/data/bz2/etcher-test-9.img.bz2 deleted file mode 100644 index 1f13cba0..00000000 Binary files a/tests/image-stream/data/bz2/etcher-test-9.img.bz2 and /dev/null differ diff --git a/tests/image-stream/data/bz2/etcher-test.img.bz2 b/tests/image-stream/data/bz2/etcher-test.img.bz2 deleted file mode 100644 index c2330c6d..00000000 Binary files a/tests/image-stream/data/bz2/etcher-test.img.bz2 and /dev/null differ diff --git a/tests/image-stream/data/dmg/etcher-test-adc.dmg b/tests/image-stream/data/dmg/etcher-test-adc.dmg deleted file mode 100644 index e7828e78..00000000 Binary files a/tests/image-stream/data/dmg/etcher-test-adc.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/etcher-test-bz2.dmg b/tests/image-stream/data/dmg/etcher-test-bz2.dmg deleted file mode 100644 index 1c634fdd..00000000 Binary files a/tests/image-stream/data/dmg/etcher-test-bz2.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/etcher-test-lzfse.dmg b/tests/image-stream/data/dmg/etcher-test-lzfse.dmg deleted file mode 100644 index 66370e18..00000000 Binary files a/tests/image-stream/data/dmg/etcher-test-lzfse.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/etcher-test-raw.dmg b/tests/image-stream/data/dmg/etcher-test-raw.dmg deleted file mode 100644 index ec2c4e5f..00000000 Binary files a/tests/image-stream/data/dmg/etcher-test-raw.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/etcher-test-zlib.dmg b/tests/image-stream/data/dmg/etcher-test-zlib.dmg deleted file mode 100644 index fe63f093..00000000 Binary files a/tests/image-stream/data/dmg/etcher-test-zlib.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/raspberrypi-compressed.dmg b/tests/image-stream/data/dmg/raspberrypi-compressed.dmg deleted file mode 100644 index a110cefc..00000000 Binary files a/tests/image-stream/data/dmg/raspberrypi-compressed.dmg and /dev/null differ diff --git a/tests/image-stream/data/dmg/raspberrypi-raw.dmg b/tests/image-stream/data/dmg/raspberrypi-raw.dmg deleted file mode 100644 index b86d4797..00000000 Binary files a/tests/image-stream/data/dmg/raspberrypi-raw.dmg and /dev/null differ diff --git a/tests/image-stream/data/gz/etcher-test.img.gz b/tests/image-stream/data/gz/etcher-test.img.gz deleted file mode 100644 index 4b7da882..00000000 Binary files a/tests/image-stream/data/gz/etcher-test.img.gz and /dev/null differ diff --git a/tests/image-stream/data/images/etcher-gpt-test-partitions.json b/tests/image-stream/data/images/etcher-gpt-test-partitions.json deleted file mode 100644 index 6f0e65c1..00000000 --- a/tests/image-stream/data/images/etcher-gpt-test-partitions.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "type": "E3C9E316-0B5C-4DB8-817D-F92DF00215AE", - "id": "F2020024-6D12-43A7-B0AA-0E243771ED00", - "name": "Microsoft reserved partition", - "firstLBA": 34, - "lastLBA": 65569, - "extended": false - }, - { - "type": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", - "id": "3B781D99-BEFA-41F7-85C7-01346507805C", - "name": "Basic data partition", - "firstLBA": 65664, - "lastLBA": 163967, - "extended": false - }, - { - "type": "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", - "id": "EE0EAF80-24C1-4A41-949E-419676E89AD6", - "name": "Basic data partition", - "firstLBA": 163968, - "lastLBA": 258175, - "extended": false - } -] diff --git a/tests/image-stream/data/images/etcher-gpt-test.img.gz b/tests/image-stream/data/images/etcher-gpt-test.img.gz deleted file mode 100644 index b56220fe..00000000 Binary files a/tests/image-stream/data/images/etcher-gpt-test.img.gz and /dev/null differ diff --git a/tests/image-stream/data/images/etcher-test-partitions.json b/tests/image-stream/data/images/etcher-test-partitions.json deleted file mode 100644 index c98cd630..00000000 --- a/tests/image-stream/data/images/etcher-test-partitions.json +++ /dev/null @@ -1,34 +0,0 @@ -[ - { - "type": 14, - "id": null, - "name": null, - "firstLBA": 128, - "lastLBA": 2176, - "extended": false - }, - { - "type": 14, - "id": null, - "name": null, - "firstLBA": 2176, - "lastLBA": 4224, - "extended": false - }, - { - "type": 0, - "id": null, - "name": null, - "firstLBA": 0, - "lastLBA": 0, - "extended": false - }, - { - "type": 0, - "id": null, - "name": null, - "firstLBA": 0, - "lastLBA": 0, - "extended": false - } -] diff --git a/tests/image-stream/data/images/etcher-test.img b/tests/image-stream/data/images/etcher-test.img deleted file mode 100644 index fc2623c3..00000000 Binary files a/tests/image-stream/data/images/etcher-test.img and /dev/null differ diff --git a/tests/image-stream/data/images/etcher-test.iso b/tests/image-stream/data/images/etcher-test.iso deleted file mode 100644 index fc2623c3..00000000 Binary files a/tests/image-stream/data/images/etcher-test.iso and /dev/null differ diff --git a/tests/image-stream/data/metadata/zip/etcher-test-invalid-manifest.zip b/tests/image-stream/data/metadata/zip/etcher-test-invalid-manifest.zip deleted file mode 100644 index 0e2cfdd0..00000000 Binary files a/tests/image-stream/data/metadata/zip/etcher-test-invalid-manifest.zip and /dev/null differ diff --git a/tests/image-stream/data/metadata/zip/etcher-test-with-bmap.zip b/tests/image-stream/data/metadata/zip/etcher-test-with-bmap.zip deleted file mode 100644 index eaa502a6..00000000 Binary files a/tests/image-stream/data/metadata/zip/etcher-test-with-bmap.zip and /dev/null differ diff --git a/tests/image-stream/data/metadata/zip/etcher-test-with-instructions.zip b/tests/image-stream/data/metadata/zip/etcher-test-with-instructions.zip deleted file mode 100644 index 0978b54c..00000000 Binary files a/tests/image-stream/data/metadata/zip/etcher-test-with-instructions.zip and /dev/null differ diff --git a/tests/image-stream/data/metadata/zip/etcher-test-with-logo.zip b/tests/image-stream/data/metadata/zip/etcher-test-with-logo.zip deleted file mode 100644 index 9a09d783..00000000 Binary files a/tests/image-stream/data/metadata/zip/etcher-test-with-logo.zip and /dev/null differ diff --git a/tests/image-stream/data/metadata/zip/etcher-test-with-manifest.zip b/tests/image-stream/data/metadata/zip/etcher-test-with-manifest.zip deleted file mode 100644 index f7c00eaf..00000000 Binary files a/tests/image-stream/data/metadata/zip/etcher-test-with-manifest.zip and /dev/null differ diff --git a/tests/image-stream/data/unrecognized/invalid.dmg b/tests/image-stream/data/unrecognized/invalid.dmg deleted file mode 100644 index bc70eab5..00000000 Binary files a/tests/image-stream/data/unrecognized/invalid.dmg and /dev/null differ diff --git a/tests/image-stream/data/unrecognized/random.rpi-sdcard b/tests/image-stream/data/unrecognized/random.rpi-sdcard deleted file mode 100644 index 0246d82a..00000000 Binary files a/tests/image-stream/data/unrecognized/random.rpi-sdcard and /dev/null differ diff --git a/tests/image-stream/data/unrecognized/xz-with-invalid-extension.foo b/tests/image-stream/data/unrecognized/xz-with-invalid-extension.foo deleted file mode 100644 index bf5ff41b..00000000 Binary files a/tests/image-stream/data/unrecognized/xz-with-invalid-extension.foo and /dev/null differ diff --git a/tests/image-stream/data/unrecognized/xz-without-extension b/tests/image-stream/data/unrecognized/xz-without-extension deleted file mode 100644 index bf5ff41b..00000000 Binary files a/tests/image-stream/data/unrecognized/xz-without-extension and /dev/null differ diff --git a/tests/image-stream/data/xz/etcher-test.img.xz b/tests/image-stream/data/xz/etcher-test.img.xz deleted file mode 100644 index bf5ff41b..00000000 Binary files a/tests/image-stream/data/xz/etcher-test.img.xz and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-bzip2.zip b/tests/image-stream/data/zip/zip-bzip2.zip deleted file mode 100644 index 435c4530..00000000 Binary files a/tests/image-stream/data/zip/zip-bzip2.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-deflate.zip b/tests/image-stream/data/zip/zip-deflate.zip deleted file mode 100644 index 8014c145..00000000 Binary files a/tests/image-stream/data/zip/zip-deflate.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-deflate64.zip b/tests/image-stream/data/zip/zip-deflate64.zip deleted file mode 100644 index a9fd4405..00000000 Binary files a/tests/image-stream/data/zip/zip-deflate64.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-empty.zip b/tests/image-stream/data/zip/zip-directory-empty.zip deleted file mode 100644 index 944763ab..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-empty.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-etcher-test-and-misc.zip b/tests/image-stream/data/zip/zip-directory-etcher-test-and-misc.zip deleted file mode 100644 index 00acf33a..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-etcher-test-and-misc.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-etcher-test-only.zip b/tests/image-stream/data/zip/zip-directory-etcher-test-only.zip deleted file mode 100644 index 685e43fb..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-etcher-test-only.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-multiple-images.zip b/tests/image-stream/data/zip/zip-directory-multiple-images.zip deleted file mode 100644 index f71a56e6..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-multiple-images.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-nested-misc.zip b/tests/image-stream/data/zip/zip-directory-nested-misc.zip deleted file mode 100644 index 126e2b47..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-nested-misc.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-directory-no-image-only-misc.zip b/tests/image-stream/data/zip/zip-directory-no-image-only-misc.zip deleted file mode 100644 index f796dc1f..00000000 Binary files a/tests/image-stream/data/zip/zip-directory-no-image-only-misc.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-lzma.zip b/tests/image-stream/data/zip/zip-lzma.zip deleted file mode 100644 index c4712ef4..00000000 Binary files a/tests/image-stream/data/zip/zip-lzma.zip and /dev/null differ diff --git a/tests/image-stream/data/zip/zip-ppmd.zip b/tests/image-stream/data/zip/zip-ppmd.zip deleted file mode 100644 index 7e606e6f..00000000 Binary files a/tests/image-stream/data/zip/zip-ppmd.zip and /dev/null differ diff --git a/tests/image-stream/directory.spec.js b/tests/image-stream/directory.spec.js deleted file mode 100644 index 1cd85a0d..00000000 --- a/tests/image-stream/directory.spec.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const errors = require('../../lib/shared/errors') -const imageStream = require('../../lib/sdk/image-stream/index') - -describe('ImageStream: Directory', function () { - describe('.getFromFilePath()', function () { - describe('given a directory', function () { - it('should be rejected with an error', function (done) { - imageStream.getFromFilePath(IMAGES_PATH).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(errors.getTitle(error)).to.equal('Invalid image') - m.chai.expect(errors.getDescription(error)).to.equal('The image must be a file') - m.chai.expect(errors.isUserError(error)).to.be.true - done() - }) - }) - }) - }) - - describe('.getImageMetadata()', function () { - it('should be rejected with an error', function (done) { - imageStream.getImageMetadata(IMAGES_PATH).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(errors.getTitle(error)).to.equal('Invalid image') - m.chai.expect(errors.getDescription(error)).to.equal('The image must be a file') - m.chai.expect(errors.isUserError(error)).to.be.true - done() - }) - }) - }) -}) diff --git a/tests/image-stream/dmg.spec.js b/tests/image-stream/dmg.spec.js deleted file mode 100644 index c6a859e2..00000000 --- a/tests/image-stream/dmg.spec.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const DMG_PATH = path.join(DATA_PATH, 'dmg') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: DMG', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('compression method', function () { - describe('NONE', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-raw.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - describe('UDCO (ADC)', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-adc.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - describe('UDZO (ZLIB)', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-zlib.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - describe('UDBZ (BZIP2)', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-bz2.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - // NOTE: Skipped, as LZFSE is not supported by `udif` module yet - describe.skip('ULFO (LZFSE)', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-lzfse.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - context('zlib compressed', function () { - describe('.getFromFilePath()', function () { - describe('given an dmg image', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-zlib.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(DMG_PATH, 'etcher-test-zlib.dmg') - const compressedSize = fs.statSync(path.join(DMG_PATH, 'etcher-test-zlib.dmg')).size - const uncompressedSize = fs.statSync(path.join(IMAGES_PATH, 'etcher-test.img')).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'dmg', - size: { - original: compressedSize, - final: { - estimation: false, - value: uncompressedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) - }) - - context('uncompressed', function () { - describe('.getFromFilePath()', function () { - describe('given an dmg image', function () { - tester.extractFromFilePath( - path.join(DMG_PATH, 'etcher-test-raw.dmg'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(DMG_PATH, 'etcher-test-raw.dmg') - const compressedSize = fs.statSync(path.join(DMG_PATH, 'etcher-test-raw.dmg')).size - const uncompressedSize = fs.statSync(path.join(IMAGES_PATH, 'etcher-test.img')).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'dmg', - size: { - original: compressedSize, - final: { - estimation: false, - value: uncompressedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) - }) - - context('invalid', function () { - describe('given an invalid dmg file', function () { - tester.expectError( - path.join(DATA_PATH, 'unrecognized', 'invalid.dmg'), - 'Invalid image', 'Invalid footer') - }) - }) -}) diff --git a/tests/image-stream/gz.spec.js b/tests/image-stream/gz.spec.js deleted file mode 100644 index 9960eead..00000000 --- a/tests/image-stream/gz.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const GZ_PATH = path.join(DATA_PATH, 'gz') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: GZ', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getFromFilePath()', function () { - describe('given a gz image', function () { - tester.extractFromFilePath( - path.join(GZ_PATH, 'etcher-test.img.gz'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(GZ_PATH, 'etcher-test.img.gz') - const uncompressedSize = fs.statSync(path.join(IMAGES_PATH, 'etcher-test.img')).size - const compressedSize = fs.statSync(path.join(GZ_PATH, 'etcher-test.img.gz')).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - archiveExtension: 'gz', - size: { - original: compressedSize, - final: { - estimation: true, - value: uncompressedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) -}) diff --git a/tests/image-stream/img.spec.js b/tests/image-stream/img.spec.js deleted file mode 100644 index dc650b4b..00000000 --- a/tests/image-stream/img.spec.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: IMG', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getFromFilePath()', function () { - describe('given an img image', function () { - tester.extractFromFilePath( - path.join(IMAGES_PATH, 'etcher-test.img'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - context('Master Boot Record', function () { - it('should return the correct metadata', function () { - const image = path.join(IMAGES_PATH, 'etcher-test.img') - const expectedSize = fs.statSync(image).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - size: { - original: expectedSize, - final: { - estimation: false, - value: expectedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) - - context('GUID Partition Table', function () { - it('should return the correct metadata', function () { - const image = path.join(IMAGES_PATH, 'etcher-gpt-test.img.gz') - const uncompressedSize = 134217728 - const expectedSize = fs.statSync(image).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - archiveExtension: 'gz', - size: { - original: expectedSize, - final: { - estimation: true, - value: uncompressedSize - } - }, - hasMBR: true, - hasGPT: true, - partitions: require('./data/images/etcher-gpt-test-partitions.json') - }) - }) - }) - }) - }) -}) diff --git a/tests/image-stream/index.spec.js b/tests/image-stream/index.spec.js deleted file mode 100644 index 894d1d62..00000000 --- a/tests/image-stream/index.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const _ = require('lodash') -const imageStream = require('../../lib/sdk/image-stream/index') - -describe('ImageStream', function () { - describe('.supportedFileTypes', function () { - it('should be an array', function () { - m.chai.expect(_.isArray(imageStream.supportedFileTypes)).to.be.true - }) - - it('should not be empty', function () { - m.chai.expect(_.isEmpty(imageStream.supportedFileTypes)).to.be.false - }) - - it('should contain only strings', function () { - m.chai.expect(_.every(_.map(imageStream.supportedFileTypes, function (fileType) { - return _.isString(fileType.extension) && _.isString(fileType.type) - }))).to.be.true - }) - - it('should not contain empty strings', function () { - m.chai.expect(_.every(_.map(imageStream.supportedFileTypes, function (fileType) { - return !_.isEmpty(fileType.extension) && !_.isEmpty(fileType.type) - }))).to.be.true - }) - - it('should not contain a leading period in any file type extension', function () { - m.chai.expect(_.every(_.map(imageStream.supportedFileTypes, function (fileType) { - return _.first(fileType.extension) !== '.' - }))).to.be.true - }) - }) -}) diff --git a/tests/image-stream/iso.spec.js b/tests/image-stream/iso.spec.js deleted file mode 100644 index cb3f180c..00000000 --- a/tests/image-stream/iso.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: ISO', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getFromFilePath()', function () { - describe('given an iso image', function () { - tester.extractFromFilePath( - path.join(IMAGES_PATH, 'etcher-test.iso'), - path.join(IMAGES_PATH, 'etcher-test.iso')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(IMAGES_PATH, 'etcher-test.iso') - const expectedSize = fs.statSync(image).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'iso', - size: { - original: expectedSize, - final: { - estimation: false, - value: expectedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) -}) diff --git a/tests/image-stream/metadata/zip.spec.js b/tests/image-stream/metadata/zip.spec.js deleted file mode 100644 index a753080b..00000000 --- a/tests/image-stream/metadata/zip.spec.js +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const path = require('path') -const DATA_PATH = path.join(__dirname, '..', 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const ZIP_PATH = path.join(DATA_PATH, 'metadata', 'zip') -const tester = require('../tester') -const imageStream = require('../../../lib/sdk/image-stream/index') - -const testMetadataProperty = (archivePath, propertyName, expectedValue) => { - return imageStream.getFromFilePath(archivePath).then((image) => { - m.chai.expect(image[propertyName]).to.deep.equal(expectedValue) - - return imageStream.getImageMetadata(archivePath).then((metadata) => { - m.chai.expect(metadata[propertyName]).to.deep.equal(expectedValue) - }) - }) -} - -describe('ImageStream: Metadata ZIP', function () { - this.timeout(10000) - - describe('given an archive with an invalid `manifest.json`', function () { - tester.expectError( - path.join(ZIP_PATH, 'etcher-test-invalid-manifest.zip'), - 'Invalid archive manifest.json') - - describe('.getImageMetadata()', function () { - it('should be rejected with an error', function () { - const image = path.join(ZIP_PATH, 'etcher-test-invalid-manifest.zip') - - return imageStream.getImageMetadata(image).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal('Invalid archive manifest.json') - }) - }) - }) - }) - - describe('given an archive with a `manifest.json`', function () { - const archive = path.join(ZIP_PATH, 'etcher-test-with-manifest.zip') - - tester.extractFromFilePath( - archive, - path.join(IMAGES_PATH, 'etcher-test.img')) - - it('should read the manifest name property', function () { - return testMetadataProperty(archive, 'name', 'Etcher Test') - }) - - it('should read the manifest version property', function () { - return testMetadataProperty(archive, 'version', '1.0.0') - }) - - it('should read the manifest url property', function () { - return testMetadataProperty(archive, 'url', 'https://www.example.com') - }) - - it('should read the manifest supportUrl property', function () { - const expectedValue = 'https://www.example.com/support/' - return testMetadataProperty(archive, 'supportUrl', expectedValue) - }) - - it('should read the manifest releaseNotesUrl property', function () { - const expectedValue = 'http://downloads.example.com/release_notes.txt' - return testMetadataProperty(archive, 'releaseNotesUrl', expectedValue) - }) - - it('should read the manifest checksumType property', function () { - return testMetadataProperty(archive, 'checksumType', 'md5') - }) - - it('should read the manifest checksum property', function () { - return testMetadataProperty(archive, 'checksum', 'add060b285d512f56c175b76b7ef1bee') - }) - - it('should read the manifest bytesToZeroOutFromTheBeginning property', function () { - return testMetadataProperty(archive, 'bytesToZeroOutFromTheBeginning', 512) - }) - - it('should read the manifest recommendedDriveSize property', function () { - return testMetadataProperty(archive, 'recommendedDriveSize', 4294967296) - }) - }) - - describe('given an archive with a `logo.svg`', function () { - const archive = path.join(ZIP_PATH, 'etcher-test-with-logo.zip') - - const logo = [ - '', - ' Hello World', - '', - '' - ].join('\n') - - it('should read the logo contents', function () { - return testMetadataProperty(archive, 'logo', logo) - }) - }) - - describe('given an archive with a bmap file', function () { - const archive = path.join(ZIP_PATH, 'etcher-test-with-bmap.zip') - - const bmap = [ - '', - '', - ' 5242880 ', - ' 4096 ', - ' 1280 ', - ' 1280 ', - ' cc6f077565c73a46198777b259c231875df4e709 ', - ' ', - ' 0-1280 ', - ' ', - '', - '' - ].join('\n') - - it('should read the bmap contents', function () { - return testMetadataProperty(archive, 'bmap', bmap) - }) - }) - - describe('given an archive with instructions', function () { - const archive = path.join(ZIP_PATH, 'etcher-test-with-instructions.zip') - - const instructions = [ - '# Example Next Steps', - '', - 'Lorem ipsum dolor sit amet.', - '' - ].join('\n') - - it('should read the instruction contents', function () { - return testMetadataProperty(archive, 'instructions', instructions) - }) - }) -}) diff --git a/tests/image-stream/mime.spec.js b/tests/image-stream/mime.spec.js deleted file mode 100644 index bde09a11..00000000 --- a/tests/image-stream/mime.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const mime = require('../../lib/sdk/image-stream/mime') - -describe('ImageStream: MIME', function () { - describe('.getMimeTypeFromFileName()', function () { - it('should resolve application/x-bzip2 for a bz2 archive', function () { - const file = path.join(DATA_PATH, 'bz2', 'etcher-test.img.bz2') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-bzip2') - }) - }) - - it('should resolve application/x-xz for a xz archive', function () { - const file = path.join(DATA_PATH, 'xz', 'etcher-test.img.xz') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-xz') - }) - }) - - it('should resolve application/gzip for a gz archive', function () { - const file = path.join(DATA_PATH, 'gz', 'etcher-test.img.gz') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/gzip') - }) - }) - - it('should resolve application/zip for a zip archive', function () { - const file = path.join(DATA_PATH, 'zip', 'zip-directory-etcher-only.zip') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/zip') - }) - }) - - it('should resolve application/octet-stream for an uncompressed image', function () { - const file = path.join(DATA_PATH, 'images', 'etcher-test.img') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/octet-stream') - }) - }) - - it('should resolve application/x-iso9660-image for an uncompressed iso', function () { - const file = path.join(DATA_PATH, 'images', 'etcher-test.iso') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-iso9660-image') - }) - }) - - it('should resolve application/x-apple-diskimage for a compressed Apple disk image', function () { - const file = path.join(DATA_PATH, 'dmg', 'etcher-test-zlib.dmg') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-apple-diskimage') - }) - }) - - it('should resolve application/x-apple-diskimage for an uncompressed Apple disk image', function () { - const file = path.join(DATA_PATH, 'dmg', 'etcher-test-raw.dmg') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-apple-diskimage') - }) - }) - - it('should resolve application/octet-stream for an unrecognized file type', function () { - const file = path.join(DATA_PATH, 'unrecognized', 'random.rpi-sdcard') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/octet-stream') - }) - }) - - it('should resolve the correct MIME type given an invalid extension', function () { - const file = path.join(DATA_PATH, 'unrecognized', 'xz-with-invalid-extension.foo') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-xz') - }) - }) - - it('should resolve the correct MIME type given no extension', function () { - const file = path.join(DATA_PATH, 'unrecognized', 'xz-without-extension') - return mime.getMimeTypeFromFileName(file).then((type) => { - m.chai.expect(type).to.equal('application/x-xz') - }) - }) - }) -}) diff --git a/tests/image-stream/tester.js b/tests/image-stream/tester.js deleted file mode 100644 index 27e7d1d6..00000000 --- a/tests/image-stream/tester.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const _ = require('lodash') -const Bluebird = require('bluebird') -const fs = Bluebird.promisifyAll(require('fs')) -const path = require('path') -const imageStream = require('../../lib/sdk/image-stream/index') - -const doFilesContainTheSameData = (file1, file2) => { - return Bluebird.props({ - file1: fs.readFileAsync(file1), - file2: fs.readFileAsync(file2) - }).then(function (data) { - return _.isEqual(data.file1, data.file2) - }) -} - -exports.DEFAULT_IMAGE_TESTS_TIMEOUT = 20000 - -exports.expectError = function (file, errorMessage, errorDetail) { - it('should be rejected with an error', function () { - return imageStream.getFromFilePath(file).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal(errorMessage) - if (errorDetail) { - m.chai.expect(error.description).to.contain(errorDetail) - m.chai.expect(error.description).to.be.a.string - m.chai.expect(error.description.length > 0).to.be.true - } - }) - }) -} - -exports.extractFromFilePath = function (file, image) { - it('should be able to extract the image', function () { - const dirname = path.join(__dirname, 'output') - const output = path.join(dirname, path.basename(file)) - - return fs.mkdirAsync(dirname) - .catch({ code: 'EEXIST' }, _.noop) - .then(function () { - return imageStream.getFromFilePath(file) - }) - .then(function (results) { - m.chai.expect(results.path).to.equal(file) - m.chai.expect(_.isString(results.extension)).to.be.true - m.chai.expect(_.isEmpty(_.trim(results.extension))).to.be.false - - if (!_.some([ - results.size.original === fs.statSync(file).size, - results.size.original === fs.statSync(image).size - ])) { - throw new Error(`Invalid size: ${results.size.original}`) - } - - const stream = results.stream - .pipe(results.transform) - .pipe(fs.createWriteStream(output)) - - return new Bluebird((resolve, reject) => { - stream.on('error', reject) - stream.on('close', resolve) - }) - }).then(function () { - return doFilesContainTheSameData(image, output) - }).then(function (areEqual) { - m.chai.expect(areEqual).to.be.true - }).finally(function () { - return fs.unlinkAsync(output) - .catch({ code: 'ENOENT' }, _.noop) - }) - }) -} diff --git a/tests/image-stream/utils.spec.js b/tests/image-stream/utils.spec.js deleted file mode 100644 index 6ace6c56..00000000 --- a/tests/image-stream/utils.spec.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const StreamReadable = require('stream').Readable -const utils = require('../../lib/sdk/image-stream/utils') - -describe('ImageStream: Utils', function () { - describe('.extractStream()', function () { - describe('given a stream that emits data', function () { - beforeEach(function () { - this.stream = new StreamReadable() - - /* eslint-disable no-underscore-dangle */ - this.stream._read = function () { - /* eslint-enable no-underscore-dangle */ - this.push(Buffer.from('Hello', 'utf8')) - this.push(Buffer.from(' ', 'utf8')) - this.push(Buffer.from('World', 'utf8')) - this.push(null) - } - }) - - it('should yield the stream data', function () { - return utils.extractStream(this.stream).then((data) => { - m.chai.expect(data.toString()).to.equal('Hello World') - }) - }) - }) - - describe('given a stream that throws an error', function () { - beforeEach(function () { - this.stream = new StreamReadable() - - /* eslint-disable no-underscore-dangle */ - this.stream._read = function () { - /* eslint-enable no-underscore-dangle */ - this.emit('error', new Error('stream error')) - } - }) - - it('should be rejected with the error', function () { - return utils.extractStream(this.stream).catch((error) => { - m.chai.expect(error).to.be.an.instanceof(Error) - m.chai.expect(error.message).to.equal('stream error') - }) - }) - }) - }) -}) diff --git a/tests/image-stream/xz.spec.js b/tests/image-stream/xz.spec.js deleted file mode 100644 index 926db437..00000000 --- a/tests/image-stream/xz.spec.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const XZ_PATH = path.join(DATA_PATH, 'xz') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: XZ', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getFromFilePath()', function () { - describe('given a xz image', function () { - tester.extractFromFilePath( - path.join(XZ_PATH, 'etcher-test.img.xz'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(XZ_PATH, 'etcher-test.img.xz') - const compressedSize = fs.statSync(image).size - const uncompressedSize = fs.statSync(path.join(IMAGES_PATH, 'etcher-test.img')).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - archiveExtension: 'xz', - size: { - original: compressedSize, - final: { - estimation: false, - value: uncompressedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) -}) diff --git a/tests/image-stream/zip.spec.js b/tests/image-stream/zip.spec.js deleted file mode 100644 index 04754ab0..00000000 --- a/tests/image-stream/zip.spec.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2016 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const m = require('mochainon') -const fs = require('fs') -const path = require('path') -const DATA_PATH = path.join(__dirname, 'data') -const IMAGES_PATH = path.join(DATA_PATH, 'images') -const ZIP_PATH = path.join(DATA_PATH, 'zip') -const imageStream = require('../../lib/sdk/image-stream/index') -const tester = require('./tester') - -describe('ImageStream: ZIP', function () { - this.timeout(tester.DEFAULT_IMAGE_TESTS_TIMEOUT) - - describe('.getFromFilePath()', function () { - describe('given an empty zip directory', function () { - tester.expectError( - path.join(ZIP_PATH, 'zip-directory-empty.zip'), - 'Invalid archive image') - }) - - describe('given a zip directory containing only misc files', function () { - tester.expectError( - path.join(ZIP_PATH, 'zip-directory-no-image-only-misc.zip'), - 'Invalid archive image') - }) - - describe('given a zip with an unsupported compression method', function () { - tester.expectError( - path.join(ZIP_PATH, 'zip-deflate64.zip'), - 'unsupported compression method: 9') - }) - - describe('given a zip directory containing multiple images', function () { - tester.expectError( - path.join(ZIP_PATH, 'zip-directory-multiple-images.zip'), - 'Invalid archive image') - }) - - describe('given a zip directory containing only an image', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-directory-etcher-test-only.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - describe('given a zip directory containing an image and other misc files', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-directory-etcher-test-and-misc.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('compression method', function () { - context('DEFLATE', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-deflate.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - // NOTE: These tests are intentionally skipped, as the - // zip library we're currently using only supports deflate - context.skip('DEFLATE64', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-deflate64.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - context.skip('PPMD', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-ppmd.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - context.skip('BZIP2', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-bzip2.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - - context.skip('LZMA', function () { - tester.extractFromFilePath( - path.join(ZIP_PATH, 'zip-lzma.zip'), - path.join(IMAGES_PATH, 'etcher-test.img')) - }) - }) - - describe('.getImageMetadata()', function () { - it('should return the correct metadata', function () { - const image = path.join(ZIP_PATH, 'zip-directory-etcher-test-only.zip') - const expectedSize = fs.statSync(path.join(IMAGES_PATH, 'etcher-test.img')).size - - return imageStream.getImageMetadata(image).then((metadata) => { - m.chai.expect(metadata).to.deep.equal({ - path: image, - extension: 'img', - archiveExtension: 'zip', - size: { - original: expectedSize, - final: { - estimation: false, - value: expectedSize - } - }, - hasMBR: true, - hasGPT: false, - partitions: require('./data/images/etcher-test-partitions.json') - }) - }) - }) - }) -}) diff --git a/tests/shared/drive-constraints.spec.js b/tests/shared/drive-constraints.spec.js index 87c4eb81..edd501dd 100644 --- a/tests/shared/drive-constraints.spec.js +++ b/tests/shared/drive-constraints.spec.js @@ -323,13 +323,8 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size - 1, - final: { - estimation: false, - value: this.drive.size - 1 - } - } + size: this.drive.size - 1, + isSizeEstimated: false } }) @@ -338,12 +333,12 @@ describe('Shared: DriveConstraints', function () { }) it('should return true if the final size is equal to the drive size', function () { - this.image.size.final.value = this.drive.size + this.image.size = this.drive.size m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) it('should return false if the final size is greater than the drive size', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.false }) }) @@ -352,18 +347,13 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size, - final: { - estimation: false, - value: this.drive.size - } - } + size: this.drive.size, + isSizeEstimated: false } }) it('should return true if the final size is less than the drive size', function () { - this.image.size.final.value = this.drive.size - 1 + this.image.size = this.drive.size - 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) @@ -372,7 +362,7 @@ describe('Shared: DriveConstraints', function () { }) it('should return false if the final size is greater than the drive size', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.false }) }) @@ -381,23 +371,18 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size + 1, - final: { - estimation: false, - value: this.drive.size + 1 - } - } + size: this.drive.size + 1, + isSizeEstimated: false } }) it('should return true if the final size is less than the drive size', function () { - this.image.size.final.value = this.drive.size - 1 + this.image.size = this.drive.size - 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) it('should return true if the final size is equal to the drive size', function () { - this.image.size.final.value = this.drive.size + this.image.size = this.drive.size m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) @@ -412,13 +397,9 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size - 1, - final: { - estimation: true, - value: this.drive.size - 1 - } - } + size: this.drive.size - 1, + compressedSize: this.drive.size - 1, + isSizeEstimated: true } }) @@ -427,12 +408,12 @@ describe('Shared: DriveConstraints', function () { }) it('should return true if the final size is equal to the drive size', function () { - this.image.size.final.value = this.drive.size + this.image.size = this.drive.size m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) it('should return true if the final size is greater than the drive size', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) }) @@ -441,18 +422,14 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size, - final: { - estimation: true, - value: this.drive.size - } - } + size: this.drive.size, + compressedSize: this.drive.size, + isSizeEstimated: true } }) it('should return true if the final size is less than the drive size', function () { - this.image.size.final.value = this.drive.size - 1 + this.image.size = this.drive.size - 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) @@ -461,7 +438,7 @@ describe('Shared: DriveConstraints', function () { }) it('should return true if the final size is greater than the drive size', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.true }) }) @@ -470,23 +447,19 @@ describe('Shared: DriveConstraints', function () { beforeEach(function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size + 1, - final: { - estimation: true, - value: this.drive.size + 1 - } - } + size: this.drive.size + 1, + compressedSize: this.drive.size + 1, + isSizeEstimated: true } }) it('should return false if the final size is less than the drive size', function () { - this.image.size.final.value = this.drive.size - 1 + this.image.size = this.drive.size - 1 m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.false }) it('should return false if the final size is equal to the drive size', function () { - this.image.size.final.value = this.drive.size + this.image.size = this.drive.size m.chai.expect(constraints.isDriveLargeEnough(this.drive, this.image)).to.be.false }) @@ -499,13 +472,8 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is undefined', function () { const result = constraints.isDriveLargeEnough(undefined, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - } + size: 1000000000, + isSizeEstimated: false }) m.chai.expect(result).to.be.false @@ -574,13 +542,8 @@ describe('Shared: DriveConstraints', function () { isReadOnly: false }, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - }, + size: 1000000000, + isSizeEstimated: false, recommendedDriveSize: 2000000000 }) @@ -595,13 +558,8 @@ describe('Shared: DriveConstraints', function () { isReadOnly: false }, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - }, + size: 1000000000, + isSizeEstimated: false, recommendedDriveSize: 2000000000 }) @@ -616,13 +574,8 @@ describe('Shared: DriveConstraints', function () { isReadOnly: false }, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - }, + size: 1000000000, + isSizeEstimated: false, recommendedDriveSize: 2000000001 }) @@ -637,13 +590,8 @@ describe('Shared: DriveConstraints', function () { isReadOnly: false }, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - } + size: 1000000000, + isSizeEstimated: false }) m.chai.expect(result).to.be.true @@ -652,13 +600,8 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is undefined', function () { const result = constraints.isDriveSizeRecommended(undefined, { path: path.join(__dirname, 'rpi.img'), - size: { - original: 1000000000, - final: { - estimation: false, - value: 1000000000 - } - }, + size: 1000000000, + isSizeEstimated: false, recommendedDriveSize: 1000000000 }) @@ -715,52 +658,32 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is not large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is not large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) }) @@ -773,52 +696,32 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is not large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is not large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) }) @@ -837,52 +740,32 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is not large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is not large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) }) @@ -895,52 +778,32 @@ describe('Shared: DriveConstraints', function () { it('should return false if the drive is not large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is not large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 5000000000, - final: { - estimation: false, - value: 5000000000 - } - } + size: 5000000000, + isSizeEstimated: false })).to.be.false }) it('should return false if the drive is large enough and is a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.join(this.mountpoint, 'rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.false }) it('should return true if the drive is large enough and is not a source drive', function () { m.chai.expect(constraints.isDriveValid(this.drive, { path: path.resolve(this.mountpoint, '../bar/rpi.img'), - size: { - original: 2000000000, - final: { - estimation: false, - value: 2000000000 - } - } + size: 2000000000, + isSizeEstimated: false })).to.be.true }) }) @@ -965,13 +828,8 @@ describe('Shared: DriveConstraints', function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size - 1, - final: { - estimation: false, - value: this.drive.size - 1 - } - } + size: this.drive.size - 1, + isSizeEstimated: false } }) @@ -1015,13 +873,8 @@ describe('Shared: DriveConstraints', function () { this.image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: this.drive.size - 1, - final: { - estimation: false, - value: this.drive.size - 1 - } - } + size: this.drive.size - 1, + isSizeEstimated: false } }) @@ -1080,7 +933,7 @@ describe('Shared: DriveConstraints', function () { describe('given the drive is too small', () => { it('should return the too small error', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 const result = constraints.getDriveImageCompatibilityStatuses(this.drive, this.image) const expected = [ @@ -1096,7 +949,7 @@ describe('Shared: DriveConstraints', function () { describe('given the drive size is null', () => { it('should not return the too small error', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 this.drive.size = null const result = constraints.getDriveImageCompatibilityStatuses(this.drive, this.image) @@ -1202,7 +1055,7 @@ describe('Shared: DriveConstraints', function () { describe('given a too small and system drive', () => { it('should return the too small drive error by precedence', function () { - this.image.size.final.value = this.drive.size + 1 + this.image.size = this.drive.size + 1 this.drive.isSystem = true const result = constraints.getDriveImageCompatibilityStatuses(this.drive, this.image) @@ -1302,13 +1155,8 @@ describe('Shared: DriveConstraints', function () { const image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: drives[2].size + 1, - final: { - estimation: false, - value: drives[2].size + 1 - } - }, + size: drives[2].size + 1, + isSizeEstimated: false, recommendedDriveSize: drives[5].size + 1 } @@ -1478,13 +1326,8 @@ describe('Shared: DriveConstraints', function () { const image = { path: path.join(__dirname, 'rpi.img'), - size: { - original: drives[2].size + 1, - final: { - estimation: false, - value: drives[2].size + 1 - } - }, + size: drives[2].size + 1, + isSizeEstimated: false, recommendedDriveSize: drives[5].size + 1 } diff --git a/tests/shared/supported-formats.spec.js b/tests/shared/supported-formats.spec.js index 55d361a2..b06081e4 100644 --- a/tests/shared/supported-formats.spec.js +++ b/tests/shared/supported-formats.spec.js @@ -23,8 +23,8 @@ const supportedFormats = require('../../lib/shared/supported-formats') describe('Shared: SupportedFormats', function () { describe('.getCompressedExtensions()', function () { it('should return the supported compressed extensions', function () { - const extensions = supportedFormats.getCompressedExtensions() - m.chai.expect(extensions).to.deep.equal([ 'gz', 'bz2', 'xz' ]) + const extensions = supportedFormats.getCompressedExtensions().sort() + m.chai.expect(extensions).to.deep.equal([ 'bz2', 'gz', 'xz' ].sort()) }) }) @@ -49,9 +49,9 @@ describe('Shared: SupportedFormats', function () { const archiveExtensions = supportedFormats.getArchiveExtensions() const compressedExtensions = supportedFormats.getCompressedExtensions() const nonCompressedExtensions = supportedFormats.getNonCompressedExtensions() - const expected = _.union(archiveExtensions, compressedExtensions, nonCompressedExtensions) + const expected = _.union(archiveExtensions, compressedExtensions, nonCompressedExtensions).sort() const extensions = supportedFormats.getAllExtensions() - m.chai.expect(extensions).to.deep.equal(expected) + m.chai.expect(extensions.sort()).to.deep.equal(expected) }) }) diff --git a/webpack.config.js b/webpack.config.js index 255f565b..6aaf61b7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,12 +17,16 @@ 'use strict' const _ = require('lodash') -const webpack = require('webpack') const path = require('path') const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin') const nodeExternals = require('webpack-node-externals') const commonConfig = { + mode: 'production', + optimization: { + // Minification breaks angular. + minimize: false + }, target: 'electron-main', module: { rules: [ @@ -32,7 +36,8 @@ const commonConfig = { use: { loader: 'babel-loader', options: { - presets: [ 'react', 'env', 'stage-0' ], + presets: [ '@babel/preset-react', '@babel/preset-env' ], + plugins: [ '@babel/plugin-proposal-function-bind' ], cacheDirectory: true } } @@ -52,9 +57,6 @@ const commonConfig = { plugins: [ new SimpleProgressWebpackPlugin({ format: process.env.WEBPACK_PROGRESS || 'verbose' - }), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production') }) ] } @@ -124,7 +126,7 @@ const etcherConfig = _.assign({ // on the tree (for testing purposes) or inside a generated // bundle (for production purposes), by translating // relative require paths within the bundle. - if (/\/(sdk|shared)/i.test(request) || /package\.json$/.test(request)) { + if (/\/shared/i.test(request) || /package\.json$/.test(request)) { const output = path.join(__dirname, 'generated') const dirname = path.join(context, request) const relative = path.relative(output, dirname)