diff --git a/lib/gui/app/components/image-selector/image-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx similarity index 61% rename from lib/gui/app/components/image-selector/image-selector.tsx rename to lib/gui/app/components/source-selector/source-selector.tsx index 6f30e98a..8d9af912 100644 --- a/lib/gui/app/components/image-selector/image-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -14,13 +14,16 @@ * limitations under the License. */ -import * as sdk from 'etcher-sdk'; +import { faFile, faLink } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as jsonStorageCb from 'electron-json-storage'; +import { sourceDestination } from 'etcher-sdk'; import * as _ from 'lodash'; import { GPTPartition, MBRPartition } from 'partitioninfo'; import * as path from 'path'; import * as React from 'react'; -import { Modal } from 'rendition'; -import { default as styled } from 'styled-components'; +import { ButtonProps, Card as BaseCard, Input, Modal, Txt } from 'rendition'; +import styled from 'styled-components'; import * as errors from '../../../../shared/errors'; import * as messages from '../../../../shared/messages'; @@ -35,15 +38,50 @@ import { replaceWindowsNetworkDriveLetter } from '../../os/windows-network-drive import { ChangeButton, DetailsText, - Footer, StepButton, StepNameButton, StepSelection, - Underline, } from '../../styled-components'; +import { colors } from '../../theme'; import { middleEllipsis } from '../../utils/middle-ellipsis'; import { SVGIcon } from '../svg-icon/svg-icon'; +const jsonStorage = { + get: (key: string) => { + return new Promise((resolve, reject) => { + jsonStorageCb.get(key, (err, value) => { + if (err) { + reject(err); + throw err; + } + resolve(value); + return value; + }); + }); + }, + set: (key: string, value: object) => { + return new Promise((resolve, reject) => { + jsonStorageCb.set(key, value, err => { + if (err) { + reject(err); + throw err; + } + resolve(value); + return value; + }); + }); + }, +}; + +const getRecentUrlImages = () => + jsonStorage.get('recentUrlImages') as Promise; + +const Card = styled(BaseCard)` + hr { + margin: 5px 0; + } +`; + // TODO move these styles to rendition const ModalText = styled.p` a { @@ -55,16 +93,6 @@ const ModalText = styled.p` } `; -const mainSupportedExtensions = _.intersection( - ['img', 'iso', 'zip'], - supportedFormats.getAllExtensions(), -); - -const extraSupportedExtensions = _.difference( - supportedFormats.getAllExtensions(), - mainSupportedExtensions, -).sort(); - function getState() { return { hasImage: selectionState.hasImage(), @@ -73,36 +101,164 @@ function getState() { }; } -interface ImageSelectorProps { - flashing: boolean; +const URLSelector = ({ done }: { done: (imageURL: string) => void }) => { + const [imageURL, setImageURL] = React.useState(''); + const [recentImages, setRecentImages]: [ + string[], + (value: React.SetStateAction) => void, + ] = React.useState([]); + React.useEffect(() => { + const fetchRecentUrlImages = async () => { + try { + const recentUrlImages: string[] = await getRecentUrlImages(); + if (!Array.isArray(recentUrlImages)) { + setRecentImages([]); + } else { + setRecentImages(recentUrlImages); + } + } catch (err) { + console.error(err); + } + }; + fetchRecentUrlImages(); + }, []); + return ( + { + const sanitizedRecentUrls = _.uniq( + _.reject([...recentImages, imageURL], _.isEmpty), + ); + await jsonStorage.set( + 'recentUrlImages', + _.takeRight(sanitizedRecentUrls, 5), + ); + done(imageURL); + }} + > + + {!_.isEmpty(recentImages) && ( +
+ Recent + ( + { + setImageURL(recent); + }} + > + + {_.last(_.split(recent, '/'))} - {recent} + + + ))} + /> +
+ )} +
+ ); +}; + +interface Flow { + icon: any; + onClick: (evt: MouseEvent) => void; + label: string; } -interface ImageSelectorState { +const FlowSelector = styled( + ({ flow, ...props }: { flow: Flow; props?: ButtonProps }) => { + return ( + + {flow.label} + + ); + }, +)` + border-radius: 24px; + + :enabled:hover { + background-color: ${colors.primary.background}; + color: ${colors.primary.foreground}; + + svg { + color: ${colors.primary.foreground}!important; + } + } +`; + +export type Source = + | typeof sourceDestination.File + | typeof sourceDestination.Http; + +export interface SourceOptions { + imagePath: string; + SourceType: Source; + sourceParams?: any[]; +} + +interface SourceSelectorProps { + flashing: boolean; + afterSelected: (options: SourceOptions) => void; +} + +interface SourceSelectorState { hasImage: boolean; imageName: string; imageSize: number; warning: { message: string; title: string | null } | null; showImageDetails: boolean; + showURLSelector: boolean; } -export class ImageSelector extends React.Component< - ImageSelectorProps, - ImageSelectorState +export class SourceSelector extends React.Component< + SourceSelectorProps, + SourceSelectorState > { private unsubscribe: () => void; + private afterSelected: SourceSelectorProps['afterSelected']; + public flows: Flow[]; - constructor(props: ImageSelectorProps) { + constructor(props: SourceSelectorProps) { super(props); this.state = { ...getState(), warning: null, showImageDetails: false, + showURLSelector: false, }; this.openImageSelector = this.openImageSelector.bind(this); + this.openURLSelector = this.openURLSelector.bind(this); this.reselectImage = this.reselectImage.bind(this); this.onDrop = this.onDrop.bind(this); this.showSelectedImageDetails = this.showSelectedImageDetails.bind(this); + this.afterSelected = props.afterSelected.bind(this); + + this.flows = [ + { + onClick: this.openImageSelector, + label: 'Flash from file', + icon: , + }, + { + onClick: this.openURLSelector, + label: 'Flash from URL', + icon: , + }, + ]; } public componentDidMount() { @@ -122,11 +278,11 @@ export class ImageSelector extends React.Component< flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid, }); - this.openImageSelector(); + selectionState.deselectImage(); } private selectImage( - image: sdk.sourceDestination.Metadata & { + image: sourceDestination.Metadata & { path: string; extension: string; hasMBR: boolean; @@ -203,7 +359,11 @@ export class ImageSelector extends React.Component< } } - private async selectImageByPath(imagePath: string) { + private async selectImageByPath({ + imagePath, + SourceType, + sourceParams, + }: SourceOptions) { try { imagePath = await replaceWindowsNetworkDriveLetter(imagePath); } catch (error) { @@ -220,12 +380,18 @@ export class ImageSelector extends React.Component< return; } - const source = new sdk.sourceDestination.File({ - path: imagePath, - }); + let source; + if (SourceType.name === sourceDestination.File.name) { + source = new sourceDestination.File({ + path: imagePath, + }); + } else { + source = new sourceDestination.Http(imagePath); + } + try { const innerSource = await source.getInnerSource(); - const metadata = (await innerSource.getMetadata()) as sdk.sourceDestination.Metadata & { + const metadata = (await innerSource.getMetadata()) as sourceDestination.Metadata & { hasMBR: boolean; partitions: MBRPartition[] | GPTPartition[]; path: string; @@ -241,6 +407,11 @@ export class ImageSelector extends React.Component< metadata.path = imagePath; metadata.extension = path.extname(imagePath).slice(1); this.selectImage(metadata); + this.afterSelected({ + imagePath, + SourceType, + sourceParams, + }); } catch (error) { const imageError = errors.createUserError({ title: 'Error opening image', @@ -278,7 +449,11 @@ export class ImageSelector extends React.Component< }); return; } - this.selectImageByPath(imagePath); + this.selectImageByPath({ + imagePath, + SourceType: sourceDestination.File, + sourceParams: [], + }); } catch (error) { exceptionReporter.report(error); } @@ -287,10 +462,26 @@ export class ImageSelector extends React.Component< private onDrop(event: React.DragEvent) { const [file] = event.dataTransfer.files; if (file) { - this.selectImageByPath(file.path); + this.selectImageByPath({ + imagePath: file.path, + SourceType: sourceDestination.File, + sourceParams: [], + }); } } + private openURLSelector() { + analytics.logEvent('Open image URL selector', { + applicationSessionUuid: + store.getState().toJS().applicationSessionUuid || '', + flashingWorkflowUuid: store.getState().toJS().flashingWorkflowUuid, + }); + + this.setState({ + showURLSelector: true, + }); + } + private onDragOver(event: React.DragEvent) { // Needed to get onDrop events on div elements event.preventDefault(); @@ -316,7 +507,7 @@ export class ImageSelector extends React.Component< // TODO add a visual change when dragging a file over the selector public render() { const { flashing } = this.props; - const { showImageDetails } = this.state; + const { showImageDetails, showURLSelector } = this.state; const hasImage = selectionState.hasImage(); @@ -353,7 +544,7 @@ export class ImageSelector extends React.Component< {!flashing && ( - Change + Remove )} @@ -362,15 +553,9 @@ export class ImageSelector extends React.Component< ) : ( - - Select image - -
- {mainSupportedExtensions.join(', ')}, and{' '} - - many more - -
+ {_.map(this.flows, flow => { + return ; + })}
)} @@ -413,6 +598,35 @@ export class ImageSelector extends React.Component< {selectionState.getImagePath()} )} + + {showURLSelector && ( + { + // Avoid analytics and selection state changes + // if no file was resolved from the dialog. + if (!imagePath) { + analytics.logEvent('URL selector closed', { + applicationSessionUuid: store.getState().toJS() + .applicationSessionUuid, + flashingWorkflowUuid: store.getState().toJS() + .flashingWorkflowUuid, + }); + this.setState({ + showURLSelector: false, + }); + return; + } + + this.selectImageByPath({ + imagePath, + SourceType: sourceDestination.Http, + }); + this.setState({ + showURLSelector: false, + }); + }} + /> + )} ); } diff --git a/lib/gui/app/modules/image-writer.ts b/lib/gui/app/modules/image-writer.ts index e0f5a9c2..66eeb1da 100644 --- a/lib/gui/app/modules/image-writer.ts +++ b/lib/gui/app/modules/image-writer.ts @@ -25,6 +25,7 @@ import * as path from 'path'; import * as packageJSON from '../../../../package.json'; import * as errors from '../../../shared/errors'; import * as permissions from '../../../shared/permissions'; +import { SourceOptions } from '../components/source-selector/source-selector.js'; import * as flashState from '../models/flash-state'; import * as selectionState from '../models/selection-state'; import * as settings from '../models/settings'; @@ -143,6 +144,7 @@ export function performWrite( image: string, drives: DrivelistDrive[], onProgress: sdk.multiWrite.OnProgressFunction, + source: SourceOptions, ): Promise<{ cancelled?: boolean }> { let cancelled = false; ipc.serve(); @@ -169,7 +171,7 @@ export function performWrite( trim: settings.get('trim'), }; - ipc.server.on('fail', ({ error }) => { + ipc.server.on('fail', ({ error }: { error: Error & { code: string } }) => { handleErrorLogging(error, analyticsData); }); @@ -191,6 +193,8 @@ export function performWrite( ipc.server.emit(socket, 'write', { imagePath: image, destinations: drives, + source, + SourceType: source.SourceType.name, validateWriteOnSuccess: settings.get('validateWriteOnSuccess'), trim: settings.get('trim'), unmountOnSuccess: settings.get('unmountOnSuccess'), @@ -252,6 +256,7 @@ export function performWrite( export async function flash( image: string, drives: DrivelistDrive[], + source: SourceOptions, ): Promise { if (flashState.isFlashing()) { throw new Error('There is already a flash in progress'); @@ -281,6 +286,7 @@ export async function flash( image, drives, flashState.setProgressState, + source, ); flashState.unsetFlashingFlag(result); } catch (error) { diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 07a1c10d..df5298f4 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -71,7 +71,10 @@ const getErrorMessageFromCode = (errorCode: string) => { return ''; }; -async function flashImageToDrive(goToSuccess: () => void): Promise { +async function flashImageToDrive( + goToSuccess: () => void, + source: any, +): Promise { const devices = selection.getSelectedDevices(); const image: any = selection.getImage(); const drives = _.filter(availableDrives.getDrives(), (drive: any) => { @@ -89,7 +92,7 @@ async function flashImageToDrive(goToSuccess: () => void): Promise { const iconPath = '../../assets/icon.png'; const basename = path.basename(image.path); try { - await imageWriter.flash(image.path, drives); + await imageWriter.flash(image.path, drives, source); if (!flashState.wasLastFlashCancelled()) { const flashResults: any = flashState.getFlashResults(); notification.send( @@ -119,7 +122,7 @@ async function flashImageToDrive(goToSuccess: () => void): Promise { if (!errorMessage) { error.image = basename; analytics.logException(error); - errorMessage = messages.error.genericFlashError(); + errorMessage = messages.error.genericFlashError(error); } return errorMessage; @@ -160,7 +163,11 @@ const formatSeconds = (totalSeconds: number) => { return `${minutes}m${seconds}s`; }; -export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => { +export const Flash = ({ + shouldFlashStepBeDisabled, + goToSuccess, + source, +}: any) => { const state: any = flashState.getFlashState(); const isFlashing = flashState.isFlashing(); const flashErrorCode = flashState.getLastFlashErrorCode(); @@ -179,7 +186,7 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => { return; } - setErrorMessage(await flashImageToDrive(goToSuccess)); + setErrorMessage(await flashImageToDrive(goToSuccess, source)); }; const handleFlashErrorResponse = (shouldRetry: boolean) => { @@ -215,7 +222,7 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => { return; } - setErrorMessage(await flashImageToDrive(goToSuccess)); + setErrorMessage(await flashImageToDrive(goToSuccess, source)); }; return ( @@ -299,7 +306,11 @@ export const Flash = ({ shouldFlashStepBeDisabled, goToSuccess }: any) => { done={() => handleFlashErrorResponse(true)} action={'Retry'} > - {errorMessage} + + {_.map(errorMessage.split('\n'), (message, key) => ( +

{message}

+ ))} +
)} diff --git a/lib/gui/app/pages/main/MainPage.tsx b/lib/gui/app/pages/main/MainPage.tsx index 2be480c5..f99fc6d2 100644 --- a/lib/gui/app/pages/main/MainPage.tsx +++ b/lib/gui/app/pages/main/MainPage.tsx @@ -16,25 +16,32 @@ import { faCog, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { sourceDestination } from 'etcher-sdk'; import * as _ from 'lodash'; import * as path from 'path'; import * as React from 'react'; -import { Button } from 'rendition'; +import { Flex } from 'rendition'; +import styled from 'styled-components'; import { FeaturedProject } from '../../components/featured-project/featured-project'; import FinishPage from '../../components/finish/finish'; -import { ImageSelector } from '../../components/image-selector/image-selector'; import { ReducedFlashingInfos } from '../../components/reduced-flashing-infos/reduced-flashing-infos'; import { SafeWebview } from '../../components/safe-webview/safe-webview'; import { SettingsModal } from '../../components/settings/settings'; +import { + SourceOptions, + SourceSelector, +} from '../../components/source-selector/source-selector'; import { SVGIcon } from '../../components/svg-icon/svg-icon'; import * as flashState from '../../models/flash-state'; import * as selectionState from '../../models/selection-state'; import * as settings from '../../models/settings'; import { observe } from '../../models/store'; import { open as openExternal } from '../../os/open-external/services/open-external'; -import { ThemedProvider } from '../../styled-components'; -import { colors } from '../../theme'; +import { + IconButton as BaseIcon, + ThemedProvider, +} from '../../styled-components'; import { middleEllipsis } from '../../utils/middle-ellipsis'; import { bytesToClosestUnit } from '../../../../shared/units'; @@ -42,6 +49,10 @@ import { bytesToClosestUnit } from '../../../../shared/units'; import { DriveSelector } from './DriveSelector'; import { Flash } from './Flash'; +const Icon = styled(BaseIcon)` + margin-right: 20px; +`; + function getDrivesTitle() { const drives = selectionState.getSelectedDrives(); @@ -80,6 +91,7 @@ interface MainPageState { current: 'main' | 'success'; isWebviewShowing: boolean; hideSettings: boolean; + source: SourceOptions; } export class MainPage extends React.Component< @@ -92,6 +104,10 @@ export class MainPage extends React.Component< current: 'main', isWebviewShowing: false, hideSettings: true, + source: { + imagePath: '', + SourceType: sourceDestination.File, + }, ...this.stateHelper(), }; } @@ -153,29 +169,22 @@ export class MainPage extends React.Component< right: 0, }} > - +))` + color: rgba(255, 255, 255, 0.7); + margin: auto; +`; export const ChangeButton = styled(BaseButton)` color: ${colors.primary.background}; diff --git a/lib/gui/modules/child-writer.ts b/lib/gui/modules/child-writer.ts index dd4d90d3..653734e5 100644 --- a/lib/gui/modules/child-writer.ts +++ b/lib/gui/modules/child-writer.ts @@ -20,8 +20,10 @@ import * as sdk from 'etcher-sdk'; import * as _ from 'lodash'; import * as ipc from 'node-ipc'; +import { File, Http } from 'etcher-sdk/build/source-destination'; import { toJSON } from '../../shared/errors'; import { GENERAL_ERROR, SUCCESS } from '../../shared/exit-codes'; +import { SourceOptions } from '../app/components/source-selector/source-selector'; ipc.config.id = process.env.IPC_CLIENT_ID as string; ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT as string; @@ -95,6 +97,7 @@ async function writeAndValidate( onProgress: sdk.multiWrite.OnProgressFunction, onFail: sdk.multiWrite.OnFailFunction, ): Promise { + console.log('source', source); let innerSource: sdk.sourceDestination.SourceDestination = await source.getInnerSource(); if (trim && (await innerSource.canRead())) { innerSource = new sdk.sourceDestination.ConfiguredSource({ @@ -136,6 +139,8 @@ interface WriteOptions { unmountOnSuccess: boolean; validateWriteOnSuccess: boolean; trim: boolean; + source: SourceOptions; + SourceType: string; } ipc.connectTo(IPC_SERVER_ID, () => { @@ -224,9 +229,15 @@ ipc.connectTo(IPC_SERVER_ID, () => { direct: true, }); }); - const source = new sdk.sourceDestination.File({ - path: options.imagePath, - }); + const { SourceType } = options; + let source; + if (SourceType === File.name) { + source = new File({ + path: options.imagePath, + }); + } else { + source = new Http(options.imagePath); + } try { const results = await writeAndValidate( source, diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 2fc7fc90..c0e482ff 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -114,18 +114,23 @@ } }, "@babel/runtime-corejs2": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.4.tgz", - "integrity": "sha512-hKNcmHQbBSJFnZ82ewYtWDZ3fXkP/l1XcfRtm7c8gHPM/DMecJtFFBEp7KMLZTuHwwb7RfemHdsEnd7L916Z6A==", + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.9.2.tgz", + "integrity": "sha512-ayjSOxuK2GaSDJFCtLgHnYjuMyIpViNujWrZo8GUpN60/n7juzJKK5yOo6RFVb0zdU9ACJFK+MsZrUnj3OmXMw==", "requires": { "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" }, "dependencies": { "core-js": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", - "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" } } }, @@ -447,9 +452,9 @@ "dev": true }, "@types/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.0.tgz", - "integrity": "sha512-5qqtNia+m2I0/85+pd2YzAXaTyKO8j+svirO5aN+XaQJ5+eZ8nx0jPtEWZLxCi50xwYsX10xUHetFzfb1WEs4Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.1.tgz", + "integrity": "sha512-oeUWVaAwI+xINDUx+3F2vJkl/vVB03VChFF/Gl3iQCdbcakjuoJyMOba+3BXRtnBhxZ7uBYqQBi9EpLnvSoztA==", "requires": { "@types/color-convert": "*" } @@ -492,6 +497,98 @@ "@types/domhandler": "*" } }, + "@types/electron-json-storage": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/electron-json-storage/-/electron-json-storage-4.0.0.tgz", + "integrity": "sha512-b1+VXOjCPENmXhV0q41NCsJIFwpIjPfuJk++h53O4dQhb2RBZYEMTuAnu/UC+0+PhvmysT1f5WNt8RHM3vGevA==", + "dev": true, + "requires": { + "electron": "^1.7.5" + }, + "dependencies": { + "@types/node": { + "version": "8.10.59", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", + "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "electron": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/electron/-/electron-1.8.8.tgz", + "integrity": "sha512-1f9zJehcTTGjrkb06o6ds+gsRq6SYhZJyxOk6zIWjRH8hVy03y/RzUDELzNas71f5vcvXmfGVvyjeEsadDI8tg==", + "dev": true, + "requires": { + "@types/node": "^8.0.24", + "electron-download": "^3.0.1", + "extract-zip": "^1.0.3" + } + }, + "electron-download": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", + "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "fs-extra": "^0.30.0", + "home-path": "^1.0.1", + "minimist": "^1.2.0", + "nugget": "^2.0.0", + "path-exists": "^2.1.0", + "rc": "^1.1.2", + "semver": "^5.3.0", + "sumchecker": "^1.2.0" + } + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "sumchecker": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz", + "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "es6-promise": "^4.0.5" + } + } + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -520,14 +617,14 @@ } }, "@types/json-schema": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", - "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==" + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==" }, "@types/lodash": { - "version": "4.14.144", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz", - "integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg==" + "version": "4.14.149", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", + "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" }, "@types/marked": { "version": "0.3.0", @@ -625,9 +722,9 @@ } }, "@types/react-jsonschema-form": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@types/react-jsonschema-form/-/react-jsonschema-form-1.6.6.tgz", - "integrity": "sha512-Z3mY9Kij0AFMXsswvEYLm/i+F4aRNVzRjLqAYdX4mxJcpMYJdKxH6+BnT+orXI+i9AoED0XR9DOt7ofcI5JrKQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@types/react-jsonschema-form/-/react-jsonschema-form-1.7.1.tgz", + "integrity": "sha512-mUU3efUOfEupoDSlCgJTOHEXJ90ZClQLThmoobkV6e8wnkSDP9EiUYza5br/QaJF8EAQFS/l/STfb/WDiUVr1w==", "requires": { "@types/json-schema": "*", "@types/react": "*" @@ -667,9 +764,9 @@ } }, "@types/sanitize-html": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.20.2.tgz", - "integrity": "sha512-SrefiiBebGIhxEFkpbbYOwO1S6+zQLWAC4s4tipchlHq1aO9bp0xiapM7Zm0ml20MF+3OePWYdksB1xtneKPxg==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.22.0.tgz", + "integrity": "sha512-zRtkG+Z9ikdEyIQL6ZouqZIPWnzoioJxgxAUFOc91f+nC8yB4GOMzGpy9V3WYfmkjaVNL4zlsycwHWykFq1HNg==", "requires": { "@types/htmlparser2": "*" } @@ -746,12 +843,9 @@ } }, "@types/uuid": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.6.tgz", - "integrity": "sha512-cCdlC/1kGEZdEglzOieLDYBxHsvEOIg7kp/2FYyVR9Pxakq+Qf/inL3RKQ+PA8gOlI/NnL+fXmQH12nwcGzsHw==", - "requires": { - "@types/node": "*" - } + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.8.tgz", + "integrity": "sha512-zHWce3allXWSmRx6/AGXKCtSOA7JjeWd2L3t4aHfysNk8mouQnWCocveaT7a4IEIlPVHp81jzlnknqTgCjCLXA==" }, "@types/webpack": { "version": "4.41.2", @@ -1477,11 +1571,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -3016,9 +3105,9 @@ "dev": true }, "copy-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", - "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", "requires": { "toggle-selection": "^1.0.6" } @@ -3147,11 +3236,11 @@ } }, "crypto-random-string": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.0.1.tgz", - "integrity": "sha512-dUL0cJ4PBLanJGJQBHQUkvZ3C4q13MXzl54oRqAIiJGiNkOZ4JDwkg/SBo7daGghzlJv16yW1p/4lIQukmbedA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-3.2.0.tgz", + "integrity": "sha512-8vPu5bsKaq2uKRy3OL7h1Oo7RayAWB8sYexLKAqvCXVib8SxgbmoF1IN4QMKjBv8uI8mp5gPPMbiRah25GMrVQ==", "requires": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" } }, "css": { @@ -3515,29 +3604,29 @@ } }, "dagre": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.4.tgz", - "integrity": "sha512-Dj0csFDrWYKdavwROb9FccHfTC4fJbyF/oJdL9LNZJ8WUvl968P6PAKEriGqfbdArVJEmmfA+UyumgWEwcHU6A==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", + "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", "requires": { - "graphlib": "^2.1.7", - "lodash": "^4.17.4" + "graphlib": "^2.1.8", + "lodash": "^4.17.15" } }, - "dagre-d3-unofficial": { + "dagre-d3": { "version": "0.6.4", - "resolved": "https://registry.npmjs.org/dagre-d3-unofficial/-/dagre-d3-unofficial-0.6.4.tgz", - "integrity": "sha512-xihvMCALDS/X646WyqBSMN1kl7f0K1Urd42EKYgmyISwML1Bep1MCMm+2Q3TIJe6y8TwHKmW6oYXnP5I0J/LIg==", + "resolved": "https://registry.npmjs.org/dagre-d3/-/dagre-d3-0.6.4.tgz", + "integrity": "sha512-e/6jXeCP7/ptlAM48clmX4xTZc5Ek6T6kagS7Oz2HrYSdqcLZFLqpAfh7ldbZRFfxCZVyh61NEPR08UQRVxJzQ==", "requires": { - "d3": "^5.12", - "dagre": "^0.8.4", - "graphlib": "^2.1.7", + "d3": "^5.14", + "dagre": "^0.8.5", + "graphlib": "^2.1.8", "lodash": "^4.17.15" }, "dependencies": { "d3": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.14.2.tgz", - "integrity": "sha512-Ccipa9XrYW5N0QkP6u0Qb8kU6WekIXBiDenmZm1zLvuq/9pBBhRCJLCICEOsH5Og4B0Xw02bhqGkK5VN/oPH0w==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.15.0.tgz", + "integrity": "sha512-C+E80SL2nLLtmykZ6klwYj5rPqB5nlfN5LdWEAVdWPppqTD8taoJi2PxLZjPeYT8FFRR2yucXq+kBlOnnvZeLg==", "requires": { "d3-array": "1", "d3-axis": "1", @@ -4260,18 +4349,6 @@ "requires": { "domelementtype": "^2.0.1", "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" - } } }, "domain-browser": { @@ -4281,25 +4358,26 @@ "dev": true }, "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" }, "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", + "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", "requires": { - "domelementtype": "1" + "domelementtype": "^2.0.1" } }, "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz", + "integrity": "sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "^0.2.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0" } }, "dot-prop": { @@ -4526,6 +4604,19 @@ } } }, + "electron-json-storage": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/electron-json-storage/-/electron-json-storage-4.1.8.tgz", + "integrity": "sha512-cBaxfSVG5SLWIvgkRJyl96W2VPCvvBcLjNaOfKC1oCENzStMt2BDzh/Qfxcz2M1mK4kNnsVv//CD46jIRrqWPw==", + "requires": { + "async": "^2.0.0", + "lockfile": "^1.0.4", + "lodash": "^4.0.1", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.1", + "write-file-atomic": "^2.4.2" + } + }, "electron-mocha": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/electron-mocha/-/electron-mocha-8.2.0.tgz", @@ -5194,9 +5285,9 @@ } }, "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" }, "env-paths": { "version": "2.2.0", @@ -6586,11 +6677,11 @@ "dev": true }, "graphlib": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.7.tgz", - "integrity": "sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", "requires": { - "lodash": "^4.17.5" + "lodash": "^4.17.15" } }, "grommet": { @@ -6799,13 +6890,19 @@ } }, "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "requires": { "react-is": "^16.7.0" } }, + "home-path": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.7.tgz", + "integrity": "sha512-tM1pVa+u3ZqQwIkXcWfhUlY3HWS3TsnKsfi2OHHvnhkX52s9etyktPyy1rQotkr0euWimChDq+QkQuDe8ngUlQ==", + "dev": true + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -6890,28 +6987,14 @@ } }, "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" } }, "http-cache-semantics": { @@ -7177,8 +7260,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "in-publish": { "version": "2.0.0", @@ -7831,6 +7913,15 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, "known-css-properties": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.3.0.tgz", @@ -8195,6 +8286,14 @@ } } }, + "lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "requires": { + "signal-exit": "^3.0.2" + } + }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -8463,9 +8562,9 @@ } }, "markdown-to-jsx": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.10.3.tgz", - "integrity": "sha512-PSoUyLnW/xoW6RsxZrquSSz5eGEOTwa15H5eqp3enmrp8esmgDJmhzd6zmQ9tgAA9TxJzx1Hmf3incYU/IamoQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz", + "integrity": "sha512-RH7LCJQ4RFmPqVeZEesKaO1biRzB/k4utoofmTCp3Eiw6D7qfvK8fzZq/2bjEJAtVkfPrM5SMt5APGf2rnaKMg==", "requires": { "prop-types": "^15.6.2", "unquote": "^1.1.0" @@ -8600,15 +8699,15 @@ "dev": true }, "mermaid": { - "version": "8.4.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.4.3.tgz", - "integrity": "sha512-qgjIINkm9hqzMt1n7cZ9of1ZXhwqSOPrvuwcBMz3RBq/D+c4T/vN3RAjpW+ZINGbbhD+fMESl/xFvqI9svxt/w==", + "version": "8.4.8", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-8.4.8.tgz", + "integrity": "sha512-sumTNBFwMX7oMQgogdr3NhgTeQOiwcEsm23rQ4KHGW7tpmvMwER1S+1gjCSSnqlmM/zw7Ga7oesYCYicKboRwQ==", "requires": { "@braintree/sanitize-url": "^3.1.0", "crypto-random-string": "^3.0.1", "d3": "^5.7.0", "dagre": "^0.8.4", - "dagre-d3-unofficial": "0.6.4", + "dagre-d3": "^0.6.4", "graphlib": "^2.1.7", "he": "^1.2.0", "lodash": "^4.17.11", @@ -8618,9 +8717,9 @@ }, "dependencies": { "d3": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.14.2.tgz", - "integrity": "sha512-Ccipa9XrYW5N0QkP6u0Qb8kU6WekIXBiDenmZm1zLvuq/9pBBhRCJLCICEOsH5Og4B0Xw02bhqGkK5VN/oPH0w==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.15.0.tgz", + "integrity": "sha512-C+E80SL2nLLtmykZ6klwYj5rPqB5nlfN5LdWEAVdWPppqTD8taoJi2PxLZjPeYT8FFRR2yucXq+kBlOnnvZeLg==", "requires": { "d3-array": "1", "d3-axis": "1", @@ -9109,9 +9208,9 @@ } }, "moment-mini": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.22.1.tgz", - "integrity": "sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw==" + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment-mini/-/moment-mini-2.24.0.tgz", + "integrity": "sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ==" }, "mountutils": { "version": "1.3.19", @@ -9270,9 +9369,9 @@ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" }, "nanoid": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.7.tgz", - "integrity": "sha512-fmS3qwDldm4bE01HCIRqNk+f255CNjnAoeV3Zzzv0KemObHKqYgirVaZA9DtKcjogicWjYcHkJs4D5A8CjnuVQ==" + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" }, "nanomatch": { "version": "1.2.13", @@ -10266,9 +10365,9 @@ "dev": true }, "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", + "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -10634,9 +10733,9 @@ "integrity": "sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==" }, "react-jsonschema-form": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.0.tgz", - "integrity": "sha512-3rZZ1tCG+vtXRXEdX751t5L1c1TUGk1pvSY/4LzBrUo5FlwulnwJpkosE4BqwSRxvfMckP8YoHFQV4KjzaHGgQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.1.tgz", + "integrity": "sha512-aaDloxNAcGXOOOcdKOxxqEEn5oDlPUZgWcs8unXXB9vjBRgCF8rCm/wVSv1u2G5ih0j/BX6Ewd/WjI2g00lPdg==", "requires": { "@babel/runtime-corejs2": "^7.4.5", "ajv": "^6.7.0", @@ -10649,9 +10748,9 @@ }, "dependencies": { "core-js": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", - "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" } } }, @@ -10985,9 +11084,9 @@ "dev": true }, "rendition": { - "version": "11.24.0", - "resolved": "https://registry.npmjs.org/rendition/-/rendition-11.24.0.tgz", - "integrity": "sha512-1aFKDyuqx6myF5gcVJZYUMucaOSdGU0KnTnyBPZAdFJQcG8xLmJMy7x+4Pd5QxRyK16W16s3VUBzvQaLeSJmyA==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/rendition/-/rendition-11.24.2.tgz", + "integrity": "sha512-e24GiqiiQtzb2SiqFCn6r/peXHeHjzVEcmQTSpki694Ub3RFkOMheY+KC9hCpf7qXTN6wfv1xhEzSb9cdD82zw==", "requires": { "@types/color": "^3.0.0", "@types/json-schema": "^7.0.3", @@ -11028,9 +11127,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.6.tgz", - "integrity": "sha512-0a2X6cgN3RdPBL2MIlR6Lt0KlM7fOFsutuXcdglcOq6WvLnYXgPQSh0Mx6tO1KCAE8MxbHSOSTWDoUxRq+l3DA==" + "version": "10.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz", + "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q==" }, "color": { "version": "3.1.2", @@ -11470,19 +11569,19 @@ } }, "sanitize-html": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", - "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.22.1.tgz", + "integrity": "sha512-++IMC00KfMQc45UWZJlhWOlS9eMrME38sFG9GXfR+k6oBo9JXSYQgTOZCl9j3v/smFTRNT9XNwz5DseFdMY+2Q==", "requires": { "chalk": "^2.4.1", - "htmlparser2": "^3.10.0", + "htmlparser2": "^4.1.0", "lodash.clonedeep": "^4.5.0", "lodash.escaperegexp": "^4.1.2", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.1", - "postcss": "^7.0.5", - "srcset": "^1.0.0", + "lodash.mergewith": "^4.6.2", + "postcss": "^7.0.27", + "srcset": "^2.0.1", "xtend": "^4.0.1" }, "dependencies": { @@ -12187,9 +12286,9 @@ "dev": true }, "slugify": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.6.tgz", - "integrity": "sha512-wA9XS475ZmGNlEnYYLPReSfuz/c3VQsEMoU43mi6OnKMCdbnFXd4/Yg7J0lBv8jkPolacMpOrWEaoYxuE1+hoQ==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.4.0.tgz", + "integrity": "sha512-FtLNsMGBSRB/0JOE2A0fxlqjI6fJsgHGS13iTuVT28kViI4JjUiNqp/vyis0ZXYcMnpR3fzGNkv+6vRlI2GwdQ==" }, "snapdragon": { "version": "0.8.2", @@ -12436,13 +12535,9 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-2.0.1.tgz", + "integrity": "sha512-00kZI87TdRKwt+P8jj8UZxbfp7mK2ufxcIMWvhAOZNJTRROimpHeruWrGvCZneiuVDLqdyHefVp748ECTnyUBQ==" }, "sshpk": { "version": "1.16.1", @@ -13271,9 +13366,9 @@ "dev": true }, "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" }, "typed-error": { "version": "3.2.0", @@ -13293,9 +13388,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.20", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.20.tgz", - "integrity": "sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw==" + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" }, "udif": { "version": "0.17.0", @@ -13321,9 +13416,9 @@ } }, "uglify-js": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.1.tgz", - "integrity": "sha512-pnOF7jY82wdIhATVn87uUY/FHU+MDUdPLkmGFvGoclQmeu229eTkbG5gjGGBi3R7UuYYSEeYXY/TTY5j2aym2g==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.0.tgz", + "integrity": "sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==", "requires": { "commander": "~2.20.3", "source-map": "~0.6.1" @@ -14250,7 +14345,6 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, "requires": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", @@ -14516,4 +14610,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 23ffa30b..05b92172 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "color": "^2.0.1", "d3": "^4.13.0", "debug": "^3.1.0", + "electron-json-storage": "^4.1.8", "electron-updater": "4.0.6", "etcher-sdk": "^3.0.1", "flexboxgrid": "^6.3.0", @@ -74,7 +75,7 @@ "react": "^16.8.5", "react-dom": "^16.8.5", "redux": "^3.5.2", - "rendition": "^11.24.0", + "rendition": "^11.24.2", "request": "^2.81.0", "resin-corvus": "^2.0.5", "roboto-fontface": "^0.9.0", @@ -90,6 +91,7 @@ "@types/bindings": "^1.3.0", "@types/bluebird": "^3.5.30", "@types/chai": "^4.2.7", + "@types/electron-json-storage": "^4.0.0", "@types/mime-types": "^2.1.0", "@types/mocha": "^5.2.7", "@types/node": "^12.12.24", diff --git a/tests/gui/modules/image-writer.spec.ts b/tests/gui/modules/image-writer.spec.ts index d80fbe55..14466a85 100644 --- a/tests/gui/modules/image-writer.spec.ts +++ b/tests/gui/modules/image-writer.spec.ts @@ -16,6 +16,7 @@ import { expect } from 'chai'; import { Drive as DrivelistDrive } from 'drivelist'; +import { sourceDestination } from 'etcher-sdk'; import * as _ from 'lodash'; import * as ipc from 'node-ipc'; import { assert, SinonStub, stub } from 'sinon'; @@ -28,6 +29,12 @@ const fakeDrive: DrivelistDrive = {}; describe('Browser: imageWriter', () => { describe('.flash()', () => { + const imagePath = 'foo.img'; + const sourceOptions = { + imagePath, + SourceType: sourceDestination.File, + }; + describe('given a successful write', () => { let performWriteStub: SinonStub; @@ -51,7 +58,7 @@ describe('Browser: imageWriter', () => { sourceChecksum: '1234', }); - imageWriter.flash('foo.img', [fakeDrive]).finally(() => { + imageWriter.flash(imagePath, [fakeDrive], sourceOptions).finally(() => { expect(flashState.isFlashing()).to.be.false; }); }); @@ -62,19 +69,23 @@ describe('Browser: imageWriter', () => { sourceChecksum: '1234', }); - const writing = imageWriter.flash('foo.img', [fakeDrive]); - imageWriter.flash('foo.img', [fakeDrive]).catch(_.noop); + const writing = imageWriter.flash( + imagePath, + [fakeDrive], + sourceOptions, + ); + imageWriter.flash(imagePath, [fakeDrive], sourceOptions).catch(_.noop); writing.finally(() => { assert.calledOnce(performWriteStub); }); }); it('should reject the second flash attempt', () => { - imageWriter.flash('foo.img', [fakeDrive]); + imageWriter.flash(imagePath, [fakeDrive], sourceOptions); let rejectError: Error; imageWriter - .flash('foo.img', [fakeDrive]) + .flash(imagePath, [fakeDrive], sourceOptions) .catch(error => { rejectError = error; }) @@ -103,7 +114,7 @@ describe('Browser: imageWriter', () => { it('should set flashing to false when done', () => { imageWriter - .flash('foo.img', [fakeDrive]) + .flash(imagePath, [fakeDrive], sourceOptions) .catch(_.noop) .finally(() => { expect(flashState.isFlashing()).to.be.false; @@ -112,7 +123,7 @@ describe('Browser: imageWriter', () => { it('should set the error code in the flash results', () => { imageWriter - .flash('foo.img', [fakeDrive]) + .flash(imagePath, [fakeDrive], sourceOptions) .catch(_.noop) .finally(() => { const flashResults = flashState.getFlashResults(); @@ -128,7 +139,7 @@ describe('Browser: imageWriter', () => { let rejection: Error; imageWriter - .flash('foo.img', [fakeDrive]) + .flash(imagePath, [fakeDrive], sourceOptions) .catch(error => { rejection = error; })