Download and install updates without the help of electron-updater

This commit is contained in:
Scott Nonnenberg 2019-03-28 10:09:26 -07:00
parent 82dc723432
commit c8ea2e9463
32 changed files with 75974 additions and 518 deletions

View file

@ -4,6 +4,7 @@ coverage/**
dist/**
# Generated files
js/curve/*
js/components.js
js/libtextsecure.js
js/util_worker.js

View file

@ -21,6 +21,7 @@ ts/util/lint/exceptions.json
# Third-party files
node_modules/**
components/**
js/curve/**
js/Mp3LameEncoder.min.js
js/WebAudioRecorderMp3.js
js/libsignal-protocol-worker.js

View file

@ -905,6 +905,22 @@
"description":
"Shown in a quotation of a message containing a photo if no text was originally provided with that image"
},
"cannotUpdate": {
"message": "Cannot Update",
"description": "Shown as the title of our update error dialogs on windows"
},
"cannotUpdateDetail": {
"message":
"Signal Desktop failed to update, but there is a new version available. Please go to https://signal.org/download and install the new version manually, then either contact support or file a bug about this problem.",
"description":
"Shown if a general error happened while trying to install update package"
},
"readOnlyVolume": {
"message":
"Signal Desktop is likely in a macOS quarantine, and will not be able to auto-update. Please try moving Signal.app to /Applications with Finder.",
"description":
"Shown on MacOS if running on a read-only volume and we cannot update"
},
"ok": {
"message": "OK"
},

View file

@ -1,89 +0,0 @@
const { autoUpdater } = require('electron-updater');
const { dialog } = require('electron');
const config = require('./config');
const windowState = require('./window_state');
const hour = 60 * 60;
const autoUpdaterInterval = hour * 1000;
const RESTART_BUTTON = 0;
const LATER_BUTTON = 1;
function autoUpdateDisabled() {
return (
process.platform === 'linux' ||
process.mas ||
config.get('disableAutoUpdate')
);
}
async function checkForUpdates() {
try {
await autoUpdater.checkForUpdates();
} catch (error) {
console.log('checkForUpdates error:', error.stack);
}
}
let showingDialog = false;
function showUpdateDialog(mainWindow, messages) {
if (showingDialog || !mainWindow) {
return;
}
showingDialog = true;
const options = {
type: 'info',
buttons: [
messages.autoUpdateRestartButtonLabel.message,
messages.autoUpdateLaterButtonLabel.message,
],
title: messages.autoUpdateNewVersionTitle.message,
message: messages.autoUpdateNewVersionMessage.message,
detail: messages.autoUpdateNewVersionInstructions.message,
defaultId: LATER_BUTTON,
cancelId: RESTART_BUTTON,
};
dialog.showMessageBox(mainWindow, options, response => {
if (response === RESTART_BUTTON) {
// We delay these update calls because they don't seem to work in this
// callback - but only if the message box has a parent window.
// Fixes this bug: https://github.com/signalapp/Signal-Desktop/issues/1864
setTimeout(() => {
windowState.markShouldQuit();
autoUpdater.quitAndInstall();
}, 200);
}
showingDialog = false;
});
}
function onError(error) {
console.log('Got an error while updating:', error.stack);
}
function initialize(getMainWindow, messages) {
if (!messages) {
throw new Error('auto-update initialize needs localized messages');
}
if (autoUpdateDisabled()) {
return;
}
autoUpdater.addListener('update-downloaded', () => {
showUpdateDialog(getMainWindow(), messages);
});
autoUpdater.addListener('error', onError);
checkForUpdates();
setInterval(checkForUpdates, autoUpdaterInterval);
}
module.exports = {
initialize,
};

2
app/window_state.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
export function markShouldQuit(): void;
export function shouldQuit(): void;

View file

@ -2,7 +2,10 @@
"serverUrl": "https://textsecure-service-staging.whispersystems.org",
"cdnUrl": "https://cdn-staging.signal.org",
"contentProxyUrl": "http://contentproxy.signal.org:443",
"disableAutoUpdate": false,
"updatesUrl": "https://updates2.signal.org/desktop",
"updatesPublicKey":
"fd7dd3de7149dc0a127909fee7de0f7620ddd0de061b37a2c303e37de802a401",
"updatesEnabled": false,
"openDevTools": false,
"buildExpiration": 0,
"certificateAuthority":

View file

@ -1,5 +1,4 @@
{
"storageProfile": "development",
"disableAutoUpdate": true,
"openDevTools": true
}

View file

@ -1,5 +1,6 @@
{
"serverUrl": "https://textsecure-service.whispersystems.org",
"cdnUrl": "https://cdn.signal.org",
"serverTrustRoot": "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"
"serverTrustRoot": "BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF",
"updatesEnabled": true
}

View file

@ -1,5 +1,4 @@
{
"storageProfile": "staging",
"disableAutoUpdate": true,
"openDevTools": true
}

View file

@ -1,5 +1,4 @@
{
"storageProfile": "test",
"disableAutoUpdate": true,
"openDevTools": false
}

View file

@ -1,5 +1,4 @@
{
"storageProfile": "test",
"disableAutoUpdate": true,
"openDevTools": false
}

View file

@ -777,6 +777,8 @@
function onEmpty() {
initialLoadComplete = true;
window.readyForUpdates();
let interval = setInterval(() => {
const view = window.owsDesktopApp.appView;
if (view) {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,158 @@
/* vim: ts=4:sw=4:expandtab */
var Internal = global.Internal || {};
(function() {
'use strict';
// Insert some bytes into the emscripten memory and return a pointer
function _allocate(bytes) {
var address = Module._malloc(bytes.length);
Module.HEAPU8.set(bytes, address);
return address;
}
function _readBytes(address, length, array) {
array.set(Module.HEAPU8.subarray(address, address + length));
}
var basepoint = new Uint8Array(32);
basepoint[0] = 9;
Internal.curve25519 = {
keyPair: function(privKey) {
var priv = new Uint8Array(privKey);
priv[0] &= 248;
priv[31] &= 127;
priv[31] |= 64;
// Where to store the result
var publicKey_ptr = Module._malloc(32);
// Get a pointer to the private key
var privateKey_ptr = _allocate(priv);
// The basepoint for generating public keys
var basepoint_ptr = _allocate(basepoint);
// The return value is just 0, the operation is done in place
var err = Module._curve25519_donna(
publicKey_ptr,
privateKey_ptr,
basepoint_ptr
);
var res = new Uint8Array(32);
_readBytes(publicKey_ptr, 32, res);
Module._free(publicKey_ptr);
Module._free(privateKey_ptr);
Module._free(basepoint_ptr);
return { pubKey: res.buffer, privKey: priv.buffer };
},
sharedSecret: function(pubKey, privKey) {
// Where to store the result
var sharedKey_ptr = Module._malloc(32);
// Get a pointer to our private key
var privateKey_ptr = _allocate(new Uint8Array(privKey));
// Get a pointer to their public key, the basepoint when you're
// generating a shared secret
var basepoint_ptr = _allocate(new Uint8Array(pubKey));
// Return value is 0 here too of course
var err = Module._curve25519_donna(
sharedKey_ptr,
privateKey_ptr,
basepoint_ptr
);
var res = new Uint8Array(32);
_readBytes(sharedKey_ptr, 32, res);
Module._free(sharedKey_ptr);
Module._free(privateKey_ptr);
Module._free(basepoint_ptr);
return res.buffer;
},
sign: function(privKey, message) {
// Where to store the result
var signature_ptr = Module._malloc(64);
// Get a pointer to our private key
var privateKey_ptr = _allocate(new Uint8Array(privKey));
// Get a pointer to the message
var message_ptr = _allocate(new Uint8Array(message));
var err = Module._curve25519_sign(
signature_ptr,
privateKey_ptr,
message_ptr,
message.byteLength
);
var res = new Uint8Array(64);
_readBytes(signature_ptr, 64, res);
Module._free(signature_ptr);
Module._free(privateKey_ptr);
Module._free(message_ptr);
return res.buffer;
},
verify: function(pubKey, message, sig) {
// Get a pointer to their public key
var publicKey_ptr = _allocate(new Uint8Array(pubKey));
// Get a pointer to the signature
var signature_ptr = _allocate(new Uint8Array(sig));
// Get a pointer to the message
var message_ptr = _allocate(new Uint8Array(message));
var res = Module._curve25519_verify(
signature_ptr,
publicKey_ptr,
message_ptr,
message.byteLength
);
Module._free(publicKey_ptr);
Module._free(signature_ptr);
Module._free(message_ptr);
return res !== 0;
},
};
Internal.curve25519_async = {
keyPair: function(privKey) {
return new Promise(function(resolve) {
resolve(Internal.curve25519.keyPair(privKey));
});
},
sharedSecret: function(pubKey, privKey) {
return new Promise(function(resolve) {
resolve(Internal.curve25519.sharedSecret(pubKey, privKey));
});
},
sign: function(privKey, message) {
return new Promise(function(resolve) {
resolve(Internal.curve25519.sign(privKey, message));
});
},
verify: function(pubKey, message, sig) {
return new Promise(function(resolve, reject) {
if (Internal.curve25519.verify(pubKey, message, sig)) {
reject(new Error('Invalid signature'));
} else {
resolve();
}
});
},
};
})();

30
main.js
View file

@ -61,7 +61,7 @@ const development = config.environment === 'development';
// data directory has been set.
const attachments = require('./app/attachments');
const attachmentChannel = require('./app/attachment_channel');
const autoUpdate = require('./app/auto_update');
const updater = require('./ts/updater/index');
const createTrayIcon = require('./app/tray_icon');
const ephemeralConfig = require('./app/ephemeral_config');
const logging = require('./app/logging');
@ -383,12 +383,29 @@ function createWindow() {
// when you should delete the corresponding element.
mainWindow = null;
});
ipc.on('show-window', () => {
showWindow();
});
}
ipc.on('show-window', () => {
showWindow();
});
let updatesStarted = false;
ipc.on('ready-for-updates', async () => {
if (updatesStarted) {
return;
}
updatesStarted = true;
try {
await updater.start(getMainWindow, locale.messages, logger);
} catch (error) {
logger.error(
'Error starting update checks:',
error && error.stack ? error.stack : error
);
}
});
function openReleaseNotes() {
shell.openExternal(
`https://github.com/signalapp/Signal-Desktop/releases/tag/v${app.getVersion()}`
@ -636,6 +653,7 @@ app.on('ready', async () => {
await logging.initialize();
logger = logging.getLogger();
logger.info('app ready');
logger.info(`starting version ${packageJson.version}`);
if (!locale) {
const appLocale = process.env.NODE_ENV === 'test' ? 'en' : app.getLocale();
@ -696,8 +714,6 @@ app.on('ready', async () => {
ready = true;
autoUpdate.initialize(getMainWindow, locale.messages);
createWindow();
if (usingTrayIcon) {

View file

@ -18,6 +18,7 @@
"generate": "yarn icon-gen && yarn grunt",
"build": "build --config.extraMetadata.environment=$SIGNAL_ENV",
"build-release": "SIGNAL_ENV=production npm run build -- --config.directories.output=release",
"sign-release": "node ts/updater/generateSignature.js",
"build-module-protobuf": "pbjs --target static-module --wrap commonjs --out ts/protobuf/compiled.js protos/*.proto && pbts --out ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
"clean-module-protobuf": "rm -f ts/protobuf/compiled.d.ts ts/protobuf/compiled.js",
"build-protobuf": "yarn build-module-protobuf",
@ -43,7 +44,6 @@
"dependencies": {
"@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#2e28733b61640556b0272a3bfc78b0357daf71e6",
"@sindresorhus/is": "0.8.0",
"@types/react-virtualized": "9.18.12",
"backbone": "1.3.3",
"blob-util": "1.3.0",
"blueimp-canvas-to-blob": "3.14.0",
@ -53,7 +53,6 @@
"config": "1.28.1",
"electron-editor-context-menu": "1.1.1",
"electron-is-dev": "0.3.0",
"electron-updater": "3.2.0",
"emoji-datasource": "4.0.0",
"emoji-datasource-apple": "4.0.0",
"emoji-js": "3.4.0",
@ -68,6 +67,7 @@
"he": "1.2.0",
"intl-tel-input": "12.1.15",
"jquery": "3.3.1",
"js-yaml": "3.13.0",
"linkify-it": "2.0.3",
"lodash": "4.17.11",
"mkdirp": "0.5.1",
@ -104,25 +104,35 @@
"devDependencies": {
"@types/chai": "4.1.2",
"@types/classnames": "2.2.3",
"@types/config": "0.0.34",
"@types/filesize": "3.6.0",
"@types/fs-extra": "5.0.5",
"@types/google-libphonenumber": "7.4.14",
"@types/got": "9.4.1",
"@types/jquery": "3.3.29",
"@types/js-yaml": "3.12.0",
"@types/linkify-it": "2.0.3",
"@types/lodash": "4.14.106",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.0.0",
"@types/pify": "3.0.2",
"@types/qs": "6.5.1",
"@types/react": "16.8.5",
"@types/react-dom": "16.8.2",
"@types/react-redux": "7.0.1",
"@types/react-virtualized": "9.18.12",
"@types/redux-logger": "3.0.7",
"@types/rimraf": "2.0.2",
"@types/semver": "5.5.0",
"@types/sinon": "4.3.1",
"@types/uuid": "3.4.4",
"arraybuffer-loader": "1.0.3",
"asar": "0.14.0",
"bower": "1.8.2",
"chai": "4.1.2",
"dashdash": "1.14.1",
"electron": "4.1.2",
"electron-builder": "20.33.1",
"electron-builder": "20.40.0",
"electron-icon-maker": "0.0.3",
"eslint": "4.14.0",
"eslint-config-airbnb-base": "12.1.0",

View file

@ -76,6 +76,7 @@ window.restart = () => {
};
window.closeAbout = () => ipc.send('close-about');
window.readyForUpdates = () => ipc.send('ready-for-updates');
window.updateTrayIcon = unreadCount =>
ipc.send('update-tray-icon', unreadCount);

View file

@ -0,0 +1,77 @@
import { assert } from 'chai';
import { getUpdateFileName, getVersion } from '../../updater/common';
describe('updater/signatures', () => {
const windows = `version: 1.23.2
files:
- url: signal-desktop-win-1.23.2.exe
sha512: hhK+cVAb+QOK/Ln0RBcq8Rb1iPcUC0KZeT4NwLB25PMGoPmakY27XE1bXq4QlkASJN1EkYTbKf3oUJtcllziyQ==
size: 92020776
path: signal-desktop-win-1.23.2.exe
sha512: hhK+cVAb+QOK/Ln0RBcq8Rb1iPcUC0KZeT4NwLB25PMGoPmakY27XE1bXq4QlkASJN1EkYTbKf3oUJtcllziyQ==
releaseDate: '2019-03-29T16:58:08.210Z'
`;
const mac = `version: 1.23.2
files:
- url: signal-desktop-mac-1.23.2.zip
sha512: f4pPo3WulTVi9zBWGsJPNIlvPOTCxPibPPDmRFDoXMmFm6lqJpXZQ9DSWMJumfc4BRp4y/NTQLGYI6b4WuJwhg==
size: 105179791
blockMapSize: 111109
path: signal-desktop-mac-1.23.2.zip
sha512: f4pPo3WulTVi9zBWGsJPNIlvPOTCxPibPPDmRFDoXMmFm6lqJpXZQ9DSWMJumfc4BRp4y/NTQLGYI6b4WuJwhg==
releaseDate: '2019-03-29T16:57:16.997Z'
`;
const windowsBeta = `version: 1.23.2-beta.1
files:
- url: signal-desktop-beta-win-1.23.2-beta.1.exe
sha512: ZHM1F3y/Y6ulP5NhbFuh7t2ZCpY4lD9BeBhPV+g2B/0p/66kp0MJDeVxTgjR49OakwpMAafA1d6y2QBail4hSQ==
size: 92028656
path: signal-desktop-beta-win-1.23.2-beta.1.exe
sha512: ZHM1F3y/Y6ulP5NhbFuh7t2ZCpY4lD9BeBhPV+g2B/0p/66kp0MJDeVxTgjR49OakwpMAafA1d6y2QBail4hSQ==
releaseDate: '2019-03-29T01:56:00.544Z'
`;
const macBeta = `version: 1.23.2-beta.1
files:
- url: signal-desktop-beta-mac-1.23.2-beta.1.zip
sha512: h/01N0DD5Jw2Q6M1n4uLGLTCrMFxcn8QOPtLR3HpABsf3w9b2jFtKb56/2cbuJXP8ol8TkTDWKnRV6mnqnLBDw==
size: 105182398
blockMapSize: 110894
path: signal-desktop-beta-mac-1.23.2-beta.1.zip
sha512: h/01N0DD5Jw2Q6M1n4uLGLTCrMFxcn8QOPtLR3HpABsf3w9b2jFtKb56/2cbuJXP8ol8TkTDWKnRV6mnqnLBDw==
releaseDate: '2019-03-29T01:53:23.881Z'
`;
describe('#getVersion', () => {
it('successfully gets version', () => {
const expected = '1.23.2';
assert.strictEqual(getVersion(windows), expected);
assert.strictEqual(getVersion(mac), expected);
const expectedBeta = '1.23.2-beta.1';
assert.strictEqual(getVersion(windowsBeta), expectedBeta);
assert.strictEqual(getVersion(macBeta), expectedBeta);
});
});
describe('#getUpdateFileName', () => {
it('successfully gets version', () => {
assert.strictEqual(
getUpdateFileName(windows),
'signal-desktop-win-1.23.2.exe'
);
assert.strictEqual(
getUpdateFileName(mac),
'signal-desktop-mac-1.23.2.zip'
);
assert.strictEqual(
getUpdateFileName(windowsBeta),
'signal-desktop-beta-win-1.23.2-beta.1.exe'
);
assert.strictEqual(
getUpdateFileName(macBeta),
'signal-desktop-beta-mac-1.23.2-beta.1.zip'
);
});
});
});

View file

@ -0,0 +1,14 @@
import { assert } from 'chai';
import { keyPair, sign, verify } from '../../updater/curve';
describe('updater/curve', () => {
it('roundtrips', () => {
const message = Buffer.from('message');
const { publicKey, privateKey } = keyPair();
const signature = sign(privateKey, message);
const verified = verify(publicKey, message, signature);
assert.strictEqual(verified, true);
});
});

View file

@ -0,0 +1,206 @@
import { existsSync } from 'fs';
import { join } from 'path';
import { assert } from 'chai';
import { copy } from 'fs-extra';
import {
_getFileHash,
getSignaturePath,
loadHexFromPath,
verifySignature,
writeHexToPath,
writeSignature,
} from '../../updater/signature';
import { createTempDir, deleteTempDir } from '../../updater/common';
import { keyPair } from '../../updater/curve';
describe('updater/signatures', () => {
it('_getFileHash returns correct hash', async () => {
const filePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const expected =
'7bc77f27d92d00b4a1d57c480ca86dacc43d57bc318339c92119d1fbf6b557a5';
const hash = await _getFileHash(filePath);
assert.strictEqual(expected, Buffer.from(hash).toString('hex'));
});
it('roundtrips binary file writes', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const path = join(tempDir, 'something.bin');
const { publicKey } = keyPair();
await writeHexToPath(path, publicKey);
const fromDisk = await loadHexFromPath(path);
assert.strictEqual(
Buffer.from(fromDisk).compare(Buffer.from(publicKey)),
0
);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('roundtrips signature', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const signaturePath = getSignaturePath(updatePath);
assert.strictEqual(existsSync(signaturePath), true);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, true);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if version changes', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const brokenVersion = 'v1.23.3';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const verified = await verifySignature(
updatePath,
brokenVersion,
publicKey
);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if signature tampered with', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const signaturePath = getSignaturePath(updatePath);
const signature = Buffer.from(await loadHexFromPath(signaturePath));
signature[4] += 3;
await writeHexToPath(signaturePath, signature);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if binary file tampered with', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const brokenSourcePath = join(
__dirname,
'../../../fixtures/pixabay-Soap-Bubble-7141.mp4'
);
await copy(brokenSourcePath, updatePath);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if signed by different key', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey } = keyPair();
const { privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
});

323
ts/updater/common.ts Normal file
View file

@ -0,0 +1,323 @@
import {
createWriteStream,
statSync,
writeFile as writeFileCallback,
} from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
// @ts-ignore
import { createParser } from 'dashdash';
// @ts-ignore
import ProxyAgent from 'proxy-agent';
import { FAILSAFE_SCHEMA, safeLoad } from 'js-yaml';
import { gt } from 'semver';
import { get as getFromConfig } from 'config';
import { get, GotOptions, stream } from 'got';
import { v4 as getGuid } from 'uuid';
import pify from 'pify';
import mkdirp from 'mkdirp';
import rimraf from 'rimraf';
import { app, BrowserWindow, dialog } from 'electron';
// @ts-ignore
import * as packageJson from '../../package.json';
import { getSignatureFileName } from './signature';
export type MessagesType = {
[key: string]: {
message: string;
description?: string;
};
};
type LogFunction = (...args: Array<any>) => void;
export type LoggerType = {
fatal: LogFunction;
error: LogFunction;
warn: LogFunction;
info: LogFunction;
debug: LogFunction;
trace: LogFunction;
};
const writeFile = pify(writeFileCallback);
const mkdirpPromise = pify(mkdirp);
const rimrafPromise = pify(rimraf);
const { platform } = process;
export async function checkForUpdates(
logger: LoggerType
): Promise<{
fileName: string;
version: string;
} | null> {
const yaml = await getUpdateYaml();
const version = getVersion(yaml);
if (!version) {
logger.warn('checkForUpdates: no version extracted from downloaded yaml');
return null;
}
if (isVersionNewer(version)) {
logger.info(`checkForUpdates: found newer version ${version}`);
return {
fileName: getUpdateFileName(yaml),
version,
};
}
logger.info(
`checkForUpdates: ${version} is not newer; no new update available`
);
return null;
}
export async function downloadUpdate(
fileName: string,
logger: LoggerType
): Promise<string> {
const baseUrl = getUpdatesBase();
const updateFileUrl = `${baseUrl}/${fileName}`;
const signatureFileName = getSignatureFileName(fileName);
const signatureUrl = `${baseUrl}/${signatureFileName}`;
let tempDir;
try {
tempDir = await createTempDir();
const targetUpdatePath = join(tempDir, fileName);
const targetSignaturePath = join(tempDir, getSignatureFileName(fileName));
logger.info(`downloadUpdate: Downloading ${signatureUrl}`);
const { body } = await get(signatureUrl, getGotOptions());
await writeFile(targetSignaturePath, body);
logger.info(`downloadUpdate: Downloading ${updateFileUrl}`);
const downloadStream = stream(updateFileUrl, getGotOptions());
const writeStream = createWriteStream(targetUpdatePath);
await new Promise((resolve, reject) => {
downloadStream.on('error', error => {
reject(error);
});
downloadStream.on('end', () => {
resolve();
});
writeStream.on('error', error => {
reject(error);
});
downloadStream.pipe(writeStream);
});
return targetUpdatePath;
} catch (error) {
if (tempDir) {
await deleteTempDir(tempDir);
}
throw error;
}
}
export async function showUpdateDialog(
mainWindow: BrowserWindow,
messages: MessagesType
): Promise<boolean> {
const RESTART_BUTTON = 0;
const LATER_BUTTON = 1;
const options = {
type: 'info',
buttons: [
messages.autoUpdateRestartButtonLabel.message,
messages.autoUpdateLaterButtonLabel.message,
],
title: messages.autoUpdateNewVersionTitle.message,
message: messages.autoUpdateNewVersionMessage.message,
detail: messages.autoUpdateNewVersionInstructions.message,
defaultId: LATER_BUTTON,
cancelId: RESTART_BUTTON,
};
return new Promise(resolve => {
dialog.showMessageBox(mainWindow, options, response => {
if (response === RESTART_BUTTON) {
// It's key to delay any install calls here because they don't seem to work inside this
// callback - but only if the message box has a parent window.
// Fixes this: https://github.com/signalapp/Signal-Desktop/issues/1864
resolve(true);
return;
}
resolve(false);
});
});
}
export async function showCannotUpdateDialog(
mainWindow: BrowserWindow,
messages: MessagesType
): Promise<boolean> {
const options = {
type: 'error',
buttons: [messages.ok.message],
title: messages.cannotUpdate.message,
message: messages.cannotUpdateDetail.message,
};
return new Promise(resolve => {
dialog.showMessageBox(mainWindow, options, () => {
resolve();
});
});
}
// Helper functions
export function getUpdateCheckUrl(): string {
return `${getUpdatesBase()}/${getUpdatesFileName()}`;
}
export function getUpdatesBase(): string {
return getFromConfig('updatesUrl');
}
export function getCertificateAuthority(): string {
return getFromConfig('certificateAuthority');
}
export function getProxyUrl(): string | undefined {
return process.env.HTTPS_PROXY || process.env.https_proxy;
}
export function getUpdatesFileName(): string {
const prefix = isBetaChannel() ? 'beta' : 'latest';
if (platform === 'darwin') {
return `${prefix}-mac.yml`;
} else {
return `${prefix}.yml`;
}
}
const hasBeta = /beta/i;
function isBetaChannel(): boolean {
return hasBeta.test(packageJson.version);
}
function isVersionNewer(newVersion: string): boolean {
const { version } = packageJson;
return gt(newVersion, version);
}
export function getVersion(yaml: string): string | undefined {
const info = parseYaml(yaml);
if (info && info.version) {
return info.version;
}
return;
}
export function getUpdateFileName(yaml: string) {
const info = parseYaml(yaml);
if (info && info.path) {
return info.path;
}
return;
}
function parseYaml(yaml: string): any {
return safeLoad(yaml, { schema: FAILSAFE_SCHEMA, json: true });
}
async function getUpdateYaml(): Promise<string> {
const targetUrl = getUpdateCheckUrl();
const { body } = await get(targetUrl, getGotOptions());
if (!body) {
throw new Error('Got unexpected response back from update check');
}
return body.toString('utf8');
}
function getGotOptions(): GotOptions<null> {
const ca = getCertificateAuthority();
const proxyUrl = getProxyUrl();
const agent = proxyUrl ? new ProxyAgent(proxyUrl) : undefined;
return {
agent,
ca,
headers: {
'Cache-Control': 'no-cache',
'User-Agent': 'Signal Desktop (+https://signal.org/download)',
},
useElectronNet: false,
};
}
function getBaseTempDir() {
// We only use tmpdir() when this code is run outside of an Electron app (as in: tests)
return app ? join(app.getPath('userData'), 'temp') : tmpdir();
}
export async function createTempDir() {
const baseTempDir = getBaseTempDir();
const uniqueName = getGuid();
const targetDir = join(baseTempDir, uniqueName);
await mkdirpPromise(targetDir);
return targetDir;
}
export async function deleteTempDir(targetDir: string) {
const pathInfo = statSync(targetDir);
if (!pathInfo.isDirectory()) {
throw new Error(
`deleteTempDir: Cannot delete path '${targetDir}' because it is not a directory`
);
}
const baseTempDir = getBaseTempDir();
if (!targetDir.startsWith(baseTempDir)) {
throw new Error(
`deleteTempDir: Cannot delete path '${targetDir}' since it is not within base temp dir`
);
}
await rimrafPromise(targetDir);
}
export function getPrintableError(error: Error) {
return error && error.stack ? error.stack : error;
}
export async function deleteBaseTempDir() {
const baseTempDir = getBaseTempDir();
await rimrafPromise(baseTempDir);
}
export function getCliOptions<T>(options: any): T {
const parser = createParser({ options });
const cliOptions = parser.parse(process.argv);
if (cliOptions.help) {
const help = parser.help().trimRight();
// tslint:disable-next-line:no-console
console.log(help);
process.exit(0);
}
return cliOptions;
}

59
ts/updater/curve.ts Normal file
View file

@ -0,0 +1,59 @@
import { randomBytes } from 'crypto';
const g = global as any;
// Because curve wrapper will populate this
g.Internal = {};
// Because curve wrapper uses 'Module' to get at curve-provided functionality
// tslint:disable-next-line
g.Module = require('../../js/curve/curve25519_compiled');
// tslint:disable-next-line
require('../../js/curve/curve25519_wrapper');
export type BinaryType = Uint8Array | Buffer;
interface CurveType {
keyPair: (
privateKey: BinaryType
) => {
pubKey: BinaryType;
privKey: BinaryType;
};
sign: (privateKey: BinaryType, message: BinaryType) => BinaryType;
verify: (
publicKey: BinaryType,
message: BinaryType,
signature: BinaryType
) => boolean;
}
const {
keyPair: internalKeyPair,
sign: internalSign,
verify: internalVerify,
} = g.Internal.curve25519 as CurveType;
export function keyPair() {
const privateKey = randomBytes(32);
const { pubKey, privKey } = internalKeyPair(privateKey);
return {
publicKey: pubKey,
privateKey: privKey,
};
}
export function sign(privateKey: BinaryType, message: BinaryType) {
return internalSign(privateKey, message);
}
export function verify(
publicKey: BinaryType,
message: BinaryType,
signature: BinaryType
) {
const failed = internalVerify(publicKey, message, signature);
return !failed;
}

View file

@ -0,0 +1,45 @@
import { getCliOptions, getPrintableError } from './common';
import { keyPair } from './curve';
import { writeHexToPath } from './signature';
/* tslint:disable:no-console */
const OPTIONS = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.',
},
{
names: ['key', 'k'],
type: 'string',
help: 'Path where public key will go',
default: 'public.key',
},
{
names: ['private', 'p'],
type: 'string',
help: 'Path where private key will go',
default: 'private.key',
},
];
type OptionsType = {
key: string;
private: string;
};
const cliOptions = getCliOptions<OptionsType>(OPTIONS);
go(cliOptions).catch(error => {
console.error('Something went wrong!', getPrintableError(error));
});
async function go(options: OptionsType) {
const { key: publicKeyPath, private: privateKeyPath } = options;
const { publicKey, privateKey } = keyPair();
await Promise.all([
writeHexToPath(publicKeyPath, publicKey),
writeHexToPath(privateKeyPath, privateKey),
]);
}

View file

@ -0,0 +1,85 @@
import { join, resolve } from 'path';
import { readdir as readdirCallback } from 'fs';
import pify from 'pify';
import { getCliOptions, getPrintableError } from './common';
import { writeSignature } from './signature';
// @ts-ignore
import * as packageJson from '../../package.json';
const readdir = pify(readdirCallback);
/* tslint:disable:no-console */
const OPTIONS = [
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.',
},
{
names: ['private', 'p'],
type: 'string',
help: 'Path to private key file (default: ./private.key)',
default: 'private.key',
},
{
names: ['update', 'u'],
type: 'string',
help: 'Path to the update package (default: the .exe or .zip in ./release)',
},
{
names: ['version', 'v'],
type: 'string',
help: `Version number of this package (default: ${packageJson.version})`,
default: packageJson.version,
},
];
type OptionsType = {
private: string;
update: string;
version: string;
};
const cliOptions = getCliOptions<OptionsType>(OPTIONS);
go(cliOptions).catch(error => {
console.error('Something went wrong!', getPrintableError(error));
});
async function go(options: OptionsType) {
const { private: privateKeyPath, version } = options;
let { update: updatePath } = options;
if (!updatePath) {
updatePath = await findUpdatePath();
}
console.log('Signing with...');
console.log(` version: ${version}`);
console.log(` update file: ${updatePath}`);
console.log(` private key file: ${privateKeyPath}`);
await writeSignature(updatePath, version, privateKeyPath);
}
const IS_EXE = /\.exe$/;
const IS_ZIP = /\.zip$/;
async function findUpdatePath(): Promise<string> {
const releaseDir = resolve('release');
const files: Array<string> = await readdir(releaseDir);
const max = files.length;
for (let i = 0; i < max; i += 1) {
const file = files[i];
const fullPath = join(releaseDir, file);
if (IS_EXE.test(file) || IS_ZIP.test(file)) {
return fullPath;
}
}
throw new Error("No suitable file found in 'release' folder!");
}

66
ts/updater/index.ts Normal file
View file

@ -0,0 +1,66 @@
import { get as getFromConfig } from 'config';
import { BrowserWindow } from 'electron';
import { start as startMacOS } from './macos';
import { start as startWindows } from './windows';
import {
deleteBaseTempDir,
getPrintableError,
LoggerType,
MessagesType,
} from './common';
let initialized = false;
export async function start(
getMainWindow: () => BrowserWindow,
messages?: MessagesType,
logger?: LoggerType
) {
const { platform } = process;
if (initialized) {
throw new Error('updater/start: Updates have already been initialized!');
}
initialized = true;
if (!messages) {
throw new Error('updater/start: Must provide messages!');
}
if (!logger) {
throw new Error('updater/start: Must provide logger!');
}
if (autoUpdateDisabled()) {
logger.info(
'updater/start: Updates disabled - not starting new version checks'
);
return;
}
try {
await deleteBaseTempDir();
} catch (error) {
logger.error(
'updater/start: Error deleting temp dir:',
getPrintableError(error)
);
}
if (platform === 'win32') {
await startWindows(getMainWindow, messages, logger);
} else if (platform === 'darwin') {
await startMacOS(getMainWindow, messages, logger);
} else {
throw new Error('updater/start: Unsupported platform');
}
}
function autoUpdateDisabled() {
return (
process.platform === 'linux' ||
process.mas ||
!getFromConfig('updatesEnabled')
);
}

324
ts/updater/macos.ts Normal file
View file

@ -0,0 +1,324 @@
import { createReadStream, statSync } from 'fs';
import { createServer, IncomingMessage, Server, ServerResponse } from 'http';
import { AddressInfo } from 'net';
import { dirname } from 'path';
import { v4 as getGuid } from 'uuid';
import { app, autoUpdater, BrowserWindow, dialog } from 'electron';
import { get as getFromConfig } from 'config';
import { gt } from 'semver';
import {
checkForUpdates,
deleteTempDir,
downloadUpdate,
getPrintableError,
LoggerType,
MessagesType,
showCannotUpdateDialog,
showUpdateDialog,
} from './common';
import { hexToBinary, verifySignature } from './signature';
import { markShouldQuit } from '../../app/window_state';
let isChecking = false;
const SECOND = 1000;
const MINUTE = SECOND * 60;
const INTERVAL = MINUTE * 30;
export async function start(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
logger.info('macos/start: starting checks...');
loggerForQuitHandler = logger;
app.once('quit', quitHandler);
setInterval(async () => {
try {
await checkDownloadAndInstall(getMainWindow, messages, logger);
} catch (error) {
logger.error('macos/start: error:', getPrintableError(error));
}
}, INTERVAL);
await checkDownloadAndInstall(getMainWindow, messages, logger);
}
let fileName: string;
let version: string;
let updateFilePath: string;
let loggerForQuitHandler: LoggerType;
async function checkDownloadAndInstall(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
if (isChecking) {
return;
}
logger.info('checkDownloadAndInstall: checking for update...');
try {
isChecking = true;
const result = await checkForUpdates(logger);
if (!result) {
return;
}
const { fileName: newFileName, version: newVersion } = result;
if (fileName !== newFileName || !version || gt(newVersion, version)) {
deleteCache(updateFilePath, logger);
fileName = newFileName;
version = newVersion;
updateFilePath = await downloadUpdate(fileName, logger);
}
const publicKey = hexToBinary(getFromConfig('updatesPublicKey'));
const verified = verifySignature(updateFilePath, version, publicKey);
if (!verified) {
// Note: We don't delete the cache here, because we don't want to continually
// re-download the broken release. We will download it only once per launch.
throw new Error(
`checkDownloadAndInstall: Downloaded update did not pass signature verification (version: '${version}'; fileName: '${fileName}')`
);
}
try {
await handToAutoUpdate(updateFilePath, logger);
} catch (error) {
const readOnly = 'Cannot update while running on a read-only volume';
const message: string = error.message || '';
if (message.includes(readOnly)) {
logger.info('checkDownloadAndInstall: showing read-only dialog...');
await showReadOnlyDialog(getMainWindow(), messages);
} else {
logger.info(
'checkDownloadAndInstall: showing general update failure dialog...'
);
await showCannotUpdateDialog(getMainWindow(), messages);
}
throw error;
}
// At this point, closing the app will cause the update to be installed automatically
// because Squirrel has cached the update file and will do the right thing.
logger.info('checkDownloadAndInstall: showing update dialog...');
const shouldUpdate = await showUpdateDialog(getMainWindow(), messages);
if (!shouldUpdate) {
return;
}
logger.info('checkDownloadAndInstall: calling quitAndInstall...');
markShouldQuit();
autoUpdater.quitAndInstall();
} catch (error) {
logger.error('checkDownloadAndInstall: error', getPrintableError(error));
} finally {
isChecking = false;
}
}
function quitHandler() {
deleteCache(updateFilePath, loggerForQuitHandler);
}
// Helpers
function deleteCache(filePath: string | null, logger: LoggerType) {
if (filePath) {
const tempDir = dirname(filePath);
deleteTempDir(tempDir).catch(error => {
logger.error(
'quitHandler: error deleting temporary directory:',
getPrintableError(error)
);
});
}
}
async function handToAutoUpdate(
filePath: string,
logger: LoggerType
): Promise<void> {
return new Promise((resolve, reject) => {
const updateFileUrl = generateFileUrl();
const server = createServer();
let serverUrl: string;
server.on('error', (error: Error) => {
logger.error(
'handToAutoUpdate: server had error',
getPrintableError(error)
);
shutdown(server, logger);
reject(error);
});
server.on(
'request',
(request: IncomingMessage, response: ServerResponse) => {
const { url } = request;
if (url === '/') {
const absoluteUrl = `${serverUrl}${updateFileUrl}`;
writeJSONResponse(absoluteUrl, response);
return;
}
if (!url || !url.startsWith(updateFileUrl)) {
write404(url, response, logger);
return;
}
pipeUpdateToSquirrel(filePath, server, response, logger, reject);
}
);
server.listen(0, '127.0.0.1', () => {
serverUrl = getServerUrl(server);
autoUpdater.on('error', (error: Error) => {
logger.error('autoUpdater: error', getPrintableError(error));
reject(error);
});
autoUpdater.on('update-downloaded', () => {
logger.info('autoUpdater: update-downloaded event fired');
shutdown(server, logger);
resolve();
});
autoUpdater.setFeedURL({
url: serverUrl,
headers: { 'Cache-Control': 'no-cache' },
});
autoUpdater.checkForUpdates();
});
});
}
function pipeUpdateToSquirrel(
filePath: string,
server: Server,
response: ServerResponse,
logger: LoggerType,
reject: (error: Error) => void
) {
const updateFileSize = getFileSize(filePath);
const readStream = createReadStream(filePath);
response.on('error', (error: Error) => {
logger.error(
'pipeUpdateToSquirrel: update file download request had an error',
getPrintableError(error)
);
shutdown(server, logger);
reject(error);
});
readStream.on('error', (error: Error) => {
logger.error(
'pipeUpdateToSquirrel: read stream error response:',
getPrintableError(error)
);
shutdown(server, logger, response);
reject(error);
});
response.writeHead(200, {
'Content-Type': 'application/zip',
'Content-Length': updateFileSize,
});
readStream.pipe(response);
}
function writeJSONResponse(url: string, response: ServerResponse) {
const data = Buffer.from(
JSON.stringify({
url,
})
);
response.writeHead(200, {
'Content-Type': 'application/json',
'Content-Length': data.byteLength,
});
response.end(data);
}
function write404(
url: string | undefined,
response: ServerResponse,
logger: LoggerType
) {
logger.error(`write404: Squirrel requested unexpected url '${url}'`);
response.writeHead(404);
response.end();
}
function getServerUrl(server: Server) {
const address = server.address() as AddressInfo;
// tslint:disable-next-line:no-http-string
return `http://127.0.0.1:${address.port}`;
}
function generateFileUrl(): string {
return `/${getGuid()}.zip`;
}
function getFileSize(targetPath: string): number {
const { size } = statSync(targetPath);
return size;
}
function shutdown(
server: Server,
logger: LoggerType,
response?: ServerResponse
) {
try {
if (server) {
server.close();
}
} catch (error) {
logger.error('shutdown: Error closing server', getPrintableError(error));
}
try {
if (response) {
response.end();
}
} catch (endError) {
logger.error(
"shutdown: couldn't end response",
getPrintableError(endError)
);
}
}
export async function showReadOnlyDialog(
mainWindow: BrowserWindow,
messages: MessagesType
): Promise<void> {
const options = {
type: 'warning',
buttons: [messages.ok.message],
title: messages.cannotUpdate.message,
message: messages.readOnlyVolume.message,
};
return new Promise(resolve => {
dialog.showMessageBox(mainWindow, options, () => {
resolve();
});
});
}

112
ts/updater/signature.ts Normal file
View file

@ -0,0 +1,112 @@
import { createHash } from 'crypto';
import {
createReadStream,
readFile as readFileCallback,
writeFile as writeFileCallback,
} from 'fs';
import { basename, dirname, join, resolve as resolvePath } from 'path';
import pify from 'pify';
import { BinaryType, sign, verify } from './curve';
const readFile = pify(readFileCallback);
const writeFile = pify(writeFileCallback);
export async function generateSignature(
updatePackagePath: string,
version: string,
privateKeyPath: string
) {
const privateKey = await loadHexFromPath(privateKeyPath);
const message = await generateMessage(updatePackagePath, version);
return sign(privateKey, message);
}
export async function verifySignature(
updatePackagePath: string,
version: string,
publicKey: BinaryType
): Promise<boolean> {
const signaturePath = getSignaturePath(updatePackagePath);
const signature = await loadHexFromPath(signaturePath);
const message = await generateMessage(updatePackagePath, version);
return verify(publicKey, message, signature);
}
// Helper methods
async function generateMessage(
updatePackagePath: string,
version: string
): Promise<BinaryType> {
const hash = await _getFileHash(updatePackagePath);
const messageString = `${Buffer.from(hash).toString('hex')}-${version}`;
return Buffer.from(messageString);
}
export async function writeSignature(
updatePackagePath: string,
version: string,
privateKeyPath: string
) {
const signaturePath = getSignaturePath(updatePackagePath);
const signature = await generateSignature(
updatePackagePath,
version,
privateKeyPath
);
await writeHexToPath(signaturePath, signature);
}
export async function _getFileHash(
updatePackagePath: string
): Promise<BinaryType> {
const hash = createHash('sha256');
const stream = createReadStream(updatePackagePath);
return new Promise((resolve, reject) => {
stream.on('data', data => {
hash.update(data);
});
stream.on('close', () => {
resolve(hash.digest());
});
stream.on('error', error => {
reject(error);
});
});
}
export function getSignatureFileName(fileName: string) {
return `${fileName}.sig`;
}
export function getSignaturePath(updatePackagePath: string): string {
const updateFullPath = resolvePath(updatePackagePath);
const updateDir = dirname(updateFullPath);
const updateFileName = basename(updateFullPath);
return join(updateDir, getSignatureFileName(updateFileName));
}
export function hexToBinary(target: string): BinaryType {
return Buffer.from(target, 'hex');
}
export function binaryToHex(data: BinaryType): string {
return Buffer.from(data).toString('hex');
}
export async function loadHexFromPath(target: string): Promise<BinaryType> {
const hexString = await readFile(target, 'utf8');
return hexToBinary(hexString);
}
export async function writeHexToPath(target: string, data: BinaryType) {
await writeFile(target, binaryToHex(data));
}

231
ts/updater/windows.ts Normal file
View file

@ -0,0 +1,231 @@
import { dirname, join } from 'path';
import { spawn as spawnEmitter, SpawnOptions } from 'child_process';
import { readdir as readdirCallback, unlink as unlinkCallback } from 'fs';
import { app, BrowserWindow } from 'electron';
import { get as getFromConfig } from 'config';
import { gt } from 'semver';
import pify from 'pify';
import {
checkForUpdates,
deleteTempDir,
downloadUpdate,
getPrintableError,
LoggerType,
MessagesType,
showCannotUpdateDialog,
showUpdateDialog,
} from './common';
import { hexToBinary, verifySignature } from './signature';
import { markShouldQuit } from '../../app/window_state';
const readdir = pify(readdirCallback);
const unlink = pify(unlinkCallback);
let isChecking = false;
const SECOND = 1000;
const MINUTE = SECOND * 60;
const INTERVAL = MINUTE * 30;
export async function start(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
logger.info('windows/start: starting checks...');
loggerForQuitHandler = logger;
app.once('quit', quitHandler);
setInterval(async () => {
try {
await checkDownloadAndInstall(getMainWindow, messages, logger);
} catch (error) {
logger.error('windows/start: error:', getPrintableError(error));
}
}, INTERVAL);
await deletePreviousInstallers(logger);
await checkDownloadAndInstall(getMainWindow, messages, logger);
}
let fileName: string;
let version: string;
let updateFilePath: string;
let installing: boolean;
let loggerForQuitHandler: LoggerType;
async function checkDownloadAndInstall(
getMainWindow: () => BrowserWindow,
messages: MessagesType,
logger: LoggerType
) {
if (isChecking) {
return;
}
try {
isChecking = true;
logger.info('checkDownloadAndInstall: checking for update...');
const result = await checkForUpdates(logger);
if (!result) {
return;
}
const { fileName: newFileName, version: newVersion } = result;
if (fileName !== newFileName || !version || gt(newVersion, version)) {
deleteCache(updateFilePath, logger);
fileName = newFileName;
version = newVersion;
updateFilePath = await downloadUpdate(fileName, logger);
}
const publicKey = hexToBinary(getFromConfig('updatesPublicKey'));
const verified = verifySignature(updateFilePath, version, publicKey);
if (!verified) {
// Note: We don't delete the cache here, because we don't want to continually
// re-download the broken release. We will download it only once per launch.
throw new Error(
`Downloaded update did not pass signature verification (version: '${version}'; fileName: '${fileName}')`
);
}
logger.info('checkDownloadAndInstall: showing dialog...');
const shouldUpdate = await showUpdateDialog(getMainWindow(), messages);
if (!shouldUpdate) {
return;
}
try {
await verifyAndInstall(updateFilePath, version, logger);
installing = true;
} catch (error) {
logger.info(
'checkDownloadAndInstall: showing general update failure dialog...'
);
await showCannotUpdateDialog(getMainWindow(), messages);
throw error;
}
markShouldQuit();
app.quit();
} catch (error) {
logger.error('checkDownloadAndInstall: error', getPrintableError(error));
} finally {
isChecking = false;
}
}
function quitHandler() {
if (updateFilePath && !installing) {
verifyAndInstall(updateFilePath, version, loggerForQuitHandler).catch(
error => {
loggerForQuitHandler.error(
'quitHandler: error installing:',
getPrintableError(error)
);
}
);
}
}
// Helpers
// This is fixed by out new install mechanisms...
// https://github.com/signalapp/Signal-Desktop/issues/2369
// ...but we should also clean up those old installers.
const IS_EXE = /\.exe$/i;
async function deletePreviousInstallers(logger: LoggerType) {
const userDataPath = app.getPath('userData');
const files: Array<string> = await readdir(userDataPath);
await Promise.all(
files.map(async file => {
const isExe = IS_EXE.test(file);
if (!isExe) {
return;
}
const fullPath = join(userDataPath, file);
try {
await unlink(fullPath);
} catch (error) {
logger.error(`deletePreviousInstallers: couldn't delete file ${file}`);
}
})
);
}
async function verifyAndInstall(
filePath: string,
newVersion: string,
logger: LoggerType
) {
const publicKey = hexToBinary(getFromConfig('updatesPublicKey'));
const verified = verifySignature(updateFilePath, newVersion, publicKey);
if (!verified) {
throw new Error(
`Downloaded update did not pass signature verification (version: '${newVersion}'; fileName: '${fileName}')`
);
}
await install(filePath, logger);
}
async function install(filePath: string, logger: LoggerType): Promise<void> {
logger.info('windows/install: installing package...');
const args = ['--updated'];
const options = {
detached: true,
stdio: 'ignore' as 'ignore', // TypeScript considers this a plain string without help
};
try {
await spawn(filePath, args, options);
} catch (error) {
if (error.code === 'UNKNOWN' || error.code === 'EACCES') {
logger.warn(
'windows/install: Error running installer; Trying again with elevate.exe'
);
await spawn(getElevatePath(), [filePath, ...args], options);
return;
}
throw error;
}
}
function deleteCache(filePath: string | null, logger: LoggerType) {
if (filePath) {
const tempDir = dirname(filePath);
deleteTempDir(tempDir).catch(error => {
logger.error(
'deleteCache: error deleting temporary directory',
getPrintableError(error)
);
});
}
}
function getElevatePath() {
const installPath = app.getAppPath();
return join(installPath, 'resources', 'elevate.exe');
}
async function spawn(
exe: string,
args: Array<string>,
options: SpawnOptions
): Promise<void> {
return new Promise((resolve, reject) => {
const emitter = spawnEmitter(exe, args, options);
emitter.on('error', reject);
emitter.unref();
// tslint:disable-next-line no-string-based-set-timeout
setTimeout(resolve, 200);
});
}

View file

@ -2814,86 +2814,6 @@
"updated": "2018-09-18T19:19:27.699Z",
"reasonDetail": "What's being eval'd is a static string, with one variable: args. Args is of the form arg1, arg2, generated programmatically."
},
{
"rule": "jQuery-$(",
"path": "node_modules/dmg-builder/node_modules/app-builder-lib/out/ProtonFramework.js",
"line": "DIR=$(dirname \"$0\")",
"lineNumber": 209,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-$(",
"path": "node_modules/dmg-builder/node_modules/app-builder-lib/out/ProtonFramework.js",
"line": "DIR=$(dirname \"$0\")",
"lineNumber": 223,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-$(",
"path": "node_modules/dmg-builder/node_modules/app-builder-lib/out/targets/MsiTarget.js",
"line": " result += `\\n${fileSpace} <File Name=\"${fileName}\" Source=\"$(var.appDir)${path.sep}${packagePath}\" ReadOnly=\"yes\" KeyPath=\"yes\"`;",
"lineNumber": 350,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-$(",
"path": "node_modules/dmg-builder/node_modules/app-builder-lib/out/targets/nsis/nsisLicense.js",
"line": " licensePage.push('!insertmacro MUI_PAGE_LICENSE \"$(MUILicense)\"');",
"lineNumber": 100,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/dmg-builder/node_modules/debug/dist/debug.js",
"line": " createDebug.enable(createDebug.load());",
"lineNumber": 721,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T02:42:07.830Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/dmg-builder/node_modules/debug/dist/debug.js",
"line": " function load() {",
"lineNumber": 855,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T02:42:07.830Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/dmg-builder/node_modules/debug/src/browser.js",
"line": "function load() {",
"lineNumber": 211,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T02:42:07.830Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/dmg-builder/node_modules/debug/src/common.js",
"line": "\tcreateDebug.enable(createDebug.load());",
"lineNumber": 261,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T02:42:07.830Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/dmg-builder/node_modules/debug/src/node.js",
"line": "function load() {",
"lineNumber": 216,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T02:42:07.830Z"
},
{
"rule": "jQuery-after(",
"path": "node_modules/dmg-builder/node_modules/mime/src/test.js",
"line": " after(function() {",
"lineNumber": 133,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-$(",
"path": "node_modules/dotenv-expand/lib/main.js",
@ -2918,46 +2838,6 @@
"reasonCategory": "falseMatch",
"updated": "2018-09-19T18:13:29.628Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/electron-updater/node_modules/debug/dist/debug.js",
"line": " createDebug.enable(createDebug.load());",
"lineNumber": 721,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/electron-updater/node_modules/debug/dist/debug.js",
"line": " function load() {",
"lineNumber": 855,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/electron-updater/node_modules/debug/src/browser.js",
"line": "function load() {",
"lineNumber": 211,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/electron-updater/node_modules/debug/src/common.js",
"line": "\tcreateDebug.enable(createDebug.load());",
"lineNumber": 261,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/electron-updater/node_modules/debug/src/node.js",
"line": "function load() {",
"lineNumber": 216,
"reasonCategory": "falseMatch",
"updated": "2019-03-22T19:00:07.131Z"
},
{
"rule": "eval",
"path": "node_modules/electron/electron.d.ts",
@ -2974,6 +2854,54 @@
"reasonCategory": "falseMatch",
"updated": "2019-02-22T01:08:09.603Z"
},
{
"rule": "jQuery-wrap(",
"path": "node_modules/electron/node_modules/@types/node/globals.d.ts",
"line": " wrap(oldStream: ReadableStream): this;",
"lineNumber": 573,
"reasonCategory": "otherUtilityCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-wrap(",
"path": "node_modules/electron/node_modules/@types/node/globals.d.ts",
"line": " static wrap(code: string): string;",
"lineNumber": 976,
"reasonCategory": "otherUtilityCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "eval",
"path": "node_modules/electron/node_modules/@types/node/repl.d.ts",
"line": " * Default: an async wrapper for the JavaScript `eval()` function. An `eval` function can",
"lineNumber": 31,
"reasonCategory": "exampleCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "eval",
"path": "node_modules/electron/node_modules/@types/node/repl.d.ts",
"line": " * for the JavaScript `eval()` function.",
"lineNumber": 180,
"reasonCategory": "exampleCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-wrap(",
"path": "node_modules/electron/node_modules/@types/node/stream.d.ts",
"line": " wrap(oldStream: NodeJS.ReadableStream): this;",
"lineNumber": 32,
"reasonCategory": "otherUtilityCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-append(",
"path": "node_modules/electron/node_modules/@types/node/url.d.ts",
"line": " append(name: string, value: string): void;",
"lineNumber": 90,
"reasonCategory": "otherUtilityCode",
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-$(",
"path": "node_modules/emoji-js/lib/jquery.emoji.js",
@ -4463,17 +4391,17 @@
"rule": "jQuery-load(",
"path": "node_modules/js-yaml/dist/js-yaml.js",
"line": "function load(input, options) {",
"lineNumber": 2545,
"lineNumber": 2557,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T01:27:05.473Z"
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/js-yaml/dist/js-yaml.js",
"line": " return load(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));",
"lineNumber": 2568,
"lineNumber": 2580,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T01:27:05.473Z"
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-$(",
@ -4486,17 +4414,17 @@
"rule": "jQuery-load(",
"path": "node_modules/js-yaml/lib/js-yaml/loader.js",
"line": "function load(input, options) {",
"lineNumber": 1568,
"lineNumber": 1580,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T01:27:05.473Z"
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-load(",
"path": "node_modules/js-yaml/lib/js-yaml/loader.js",
"line": " return load(input, common.extend({ schema: DEFAULT_SAFE_SCHEMA }, options));",
"lineNumber": 1591,
"lineNumber": 1603,
"reasonCategory": "falseMatch",
"updated": "2019-03-13T01:27:05.473Z"
"updated": "2019-04-03T00:52:04.925Z"
},
{
"rule": "jQuery-$(",
@ -6267,4 +6195,4 @@
"updated": "2019-03-09T00:08:44.242Z",
"reasonDetail": "Used only to trigger menu display"
}
]
]

View file

@ -4,7 +4,7 @@ import { readFileSync } from 'fs';
import { join, relative } from 'path';
// @ts-ignore
import glob from 'glob';
import * as glob from 'glob';
import { forEach, some, values } from 'lodash';
import { ExceptionType, REASONS, RuleType } from './types';
@ -57,6 +57,7 @@ const excludedFiles = [
// Generated files
'^js/components.js',
'^js/curve/',
'^js/libtextsecure.js',
'^js/util_worker.js',
'^libtextsecure/components.js',

View file

@ -90,6 +90,11 @@
"allow-pascal-case"
],
"function-name": [true, { "function-regex": "^_?[a-z][\\w\\d]+$" }],
// Adding select dev dependencies here for now, may turn on all in the future
"no-implicit-dependencies": [true, ["dashdash", "electron"]],
// Maybe will turn on:
// We're not trying to be comprehensive with JSDoc right now. We have the style guide.

579
yarn.lock
View file

@ -89,16 +89,50 @@
resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.3.tgz#3f0ff6873da793870e20a260cada55982f38a9e5"
integrity sha512-x15/Io+JdzrkM9gnX6SWUs/EmqQzd65TD9tcZIAQ1VIdb93XErNuYmB7Yho8JUCE189ipUSESsWvGvYXRRIvYA==
"@types/config@0.0.34":
version "0.0.34"
resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.34.tgz#123f91bdb5afdd702294b9de9ca04d9ea11137b0"
integrity sha512-jWi9DXx77hnzN4kHCNEvP/kab+nchRLTg9yjXYxjTcMBkuc5iBb3QuwJ4sPrb+nzy1GQjrfyfMqZOdR4i7opRQ==
"@types/events@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
"@types/filesize@3.6.0":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@types/filesize/-/filesize-3.6.0.tgz#5f1a25c7b4e3d5ee2bc63133d374d096b7008c8d"
integrity sha512-rOWxCKMjt2DBuwddUnl5GOpf/jAkkqteB+XldncpVxVX+HPTmK2c5ACMOVEbp9gaH81IlhTdC3TwvRa5nopasw==
"@types/fs-extra@5.0.5":
version "5.0.5"
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.5.tgz#080d90a792f3fa2c5559eb44bd8ef840aae9104b"
integrity sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==
dependencies:
"@types/node" "*"
"@types/glob@*":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
dependencies:
"@types/events" "*"
"@types/minimatch" "*"
"@types/node" "*"
"@types/google-libphonenumber@7.4.14":
version "7.4.14"
resolved "https://registry.yarnpkg.com/@types/google-libphonenumber/-/google-libphonenumber-7.4.14.tgz#3625d7aed0c16df920588428c86f0538bd0612ec"
integrity sha512-HGQobTn8CGn0/j7lezIHlGTE1czxQU6NeT7TYtC3OWcbyyUUSfdt4tXSTEGbXmNZlE+xczTDbyR8P6aYGPzG1A==
"@types/got@9.4.1":
version "9.4.1"
resolved "https://registry.yarnpkg.com/@types/got/-/got-9.4.1.tgz#7479a3a321599b5e17647f3bd9d402b8c55bfee1"
integrity sha512-m7Uc07bG/bZ+Dis7yI3mGssYDcAdUvP4irF3ZmBzf0ig7zEd1FyADfnELVGcf+p1Ol/iPCXbZYwcSNOJA2a+Qg==
dependencies:
"@types/node" "*"
"@types/tough-cookie" "*"
"@types/jquery@3.3.29":
version "3.3.29"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd"
@ -106,6 +140,11 @@
dependencies:
"@types/sizzle" "*"
"@types/js-yaml@3.12.0":
version "3.12.0"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.0.tgz#3494ce97358e2675e24e97a747ec23478eeaf8b6"
integrity sha512-UGEe/6RsNAxgWdknhzFZbCxuYc5I7b/YEKlfKbo+76SM8CJzGs7XKCj7zyugXViRbKYpXhSXhCYVQZL5tmDbpQ==
"@types/linkify-it@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.0.3.tgz#5352a2d7a35d7c77b527483cd6e68da9148bd780"
@ -121,11 +160,28 @@
resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69"
integrity sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/mkdirp@0.5.2":
version "0.5.2"
resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f"
integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==
dependencies:
"@types/node" "*"
"@types/mocha@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.0.0.tgz#a3014921991066193f6c8e47290d4d598dfd19e6"
integrity sha512-ZS0vBV7Jn5Z/Q4T3VXauEKMDCV8nWOtJJg90OsDylkYJiQwcWtKuLzohWzrthBkerUF7DLMmJcwOPEP0i/AOXw==
"@types/node@*":
version "11.12.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.2.tgz#d7f302e74b10e9801d52852137f652d9ee235da8"
integrity sha512-c82MtnqWB/CqqK7/zit74Ob8H1dBdV7bK+BcErwtXbe0+nUGkgzq5NTDmRW/pAv2lFtmeNmW95b0zK2hxpeklg==
"@types/node@^10.12.18":
version "10.12.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.26.tgz#2dec19f1f7981c95cb54bab8f618ecb5dc983d0e"
@ -136,6 +192,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.40.tgz#4314888d5cd537945d73e9ce165c04cc550144a4"
integrity sha512-RRSjdwz63kS4u7edIwJUn8NqKLLQ6LyqF/X4+4jp38MBT3Vwetewi2N4dgJEshLbDwNgOJXNYoOwzVZUSSLhkQ==
"@types/pify@3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/pify/-/pify-3.0.2.tgz#1bc75dac43e31dba981c37e0a08edddc1b49cd39"
integrity sha512-a5AKF1/9pCU3HGMkesgY6LsBdXHUY3WU+I2qgpU0J+I8XuJA1aFr59eS84/HP0+dxsyBSNbt+4yGI2adUpHwSg==
"@types/prop-types@*":
version "15.5.9"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.9.tgz#f2d14df87b0739041bc53a7d75e3d77d726a3ec0"
@ -184,6 +245,14 @@
dependencies:
redux "^3.6.0"
"@types/rimraf@2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e"
integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ==
dependencies:
"@types/glob" "*"
"@types/node" "*"
"@types/semver@5.5.0":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
@ -199,6 +268,18 @@
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
"@types/tough-cookie@*":
version "2.3.5"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d"
integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==
"@types/uuid@3.4.4":
version "3.4.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5"
integrity sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==
dependencies:
"@types/node" "*"
"@vxna/mini-html-webpack-template@^0.1.6":
version "0.1.6"
resolved "https://registry.yarnpkg.com/@vxna/mini-html-webpack-template/-/mini-html-webpack-template-0.1.6.tgz#64225d564da5fe610b6445523c245572923c00b8"
@ -271,9 +352,10 @@ ajv-keywords@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
ajv-keywords@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
ajv-keywords@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d"
integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==
ajv@^4.9.1:
version "4.11.7"
@ -300,7 +382,7 @@ ajv@^6.1.0:
json-schema-traverse "^0.3.0"
uri-js "^3.0.2"
ajv@^6.5.5:
ajv@^6.9.2:
version "6.10.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==
@ -349,6 +431,11 @@ ansi-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
ansi-regex@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@ -376,70 +463,40 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
app-builder-bin@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-2.4.1.tgz#1e575eb0785160d87a9c6cc08d2bec2deeec511d"
integrity sha512-MZ1enBOVLujeKCi/rH3FJaIxjwAPUFRVuwHI3uG7lHj3Zyk0eP/QqFxz6PM9I9K155Yc7N/01bP1sh+ChzjxWw==
app-builder-bin@2.6.6:
version "2.6.6"
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-2.6.6.tgz#904b0576a510047d03f63c8a02c38eef47871180"
integrity sha512-G0Ee6xkbxV+fvM/7xXWIgSDjWAD4E/d/aNbxerq/TVsCyBIau/0VPmrEqBMyZv0NbTwLDW5aF/yHG+0ZEY77kA==
app-builder-lib@20.33.1:
version "20.33.1"
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-20.33.1.tgz#c604758a2bf7996fa6f5b070bf390aa816f434c3"
integrity sha512-QuacPZykldh8T7jsKnGwONXoMgnaf3f99Ay4AqLbEndfNHeBZzRUyoluxPBMp3uzNEys28q6xNWpsp0JWsWwYw==
app-builder-lib@20.40.0, app-builder-lib@~20.40.0:
version "20.40.0"
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-20.40.0.tgz#09756d28f15f45d4c4f9658ec142432cbcde377c"
integrity sha512-F1asWqNAH1l5KJlrBZh85LFJck90zYP/9raOJTvF72WATesGg4h/LpCyWsz054n8/eP/mXxmog6D7PMfd1lz5w==
dependencies:
"7zip-bin" "~4.1.0"
app-builder-bin "2.4.1"
app-builder-bin "2.6.6"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.6"
builder-util "9.0.0"
builder-util-runtime "6.1.0"
bluebird-lst "^1.0.7"
builder-util "9.7.1"
builder-util-runtime "8.2.1"
chromium-pickle-js "^0.2.0"
debug "^4.1.0"
debug "^4.1.1"
ejs "^2.6.1"
electron-osx-sign "0.4.11"
electron-publish "20.33.0"
fs-extra-p "^7.0.0"
electron-publish "20.40.0"
fs-extra-p "^7.0.1"
hosted-git-info "^2.7.1"
is-ci "^1.2.1"
isbinaryfile "^3.0.3"
js-yaml "^3.12.0"
lazy-val "^1.0.3"
is-ci "^2.0.0"
isbinaryfile "^4.0.0"
js-yaml "^3.13.0"
lazy-val "^1.0.4"
minimatch "^3.0.4"
normalize-package-data "^2.4.0"
normalize-package-data "^2.5.0"
plist "^3.0.1"
read-config-file "3.2.0"
read-config-file "3.2.2"
sanitize-filename "^1.6.1"
semver "^5.6.0"
temp-file "^3.2.0"
app-builder-lib@~20.33.0:
version "20.33.2"
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-20.33.2.tgz#3653fc512c844858228584298bd2bad97e91fd7e"
integrity sha512-RBeN0UbYYW/xdSiCLnVANhBsro2MemFAtBTib8QkwOr/uE1646tNH1JKOZ7fxhfrIQChWH3xcDSiZMa8ReB2ng==
dependencies:
"7zip-bin" "~4.1.0"
app-builder-bin "2.4.1"
async-exit-hook "^2.0.1"
bluebird-lst "^1.0.6"
builder-util "9.1.0"
builder-util-runtime "7.1.0"
chromium-pickle-js "^0.2.0"
debug "^4.1.0"
ejs "^2.6.1"
electron-osx-sign "0.4.11"
electron-publish "20.33.2"
fs-extra-p "^7.0.0"
hosted-git-info "^2.7.1"
is-ci "^1.2.1"
isbinaryfile "^3.0.3"
js-yaml "^3.12.0"
lazy-val "^1.0.3"
minimatch "^3.0.4"
normalize-package-data "^2.4.0"
plist "^3.0.1"
read-config-file "3.2.0"
sanitize-filename "^1.6.1"
semver "^5.6.0"
temp-file "^3.2.0"
semver "^6.0.0"
temp-file "^3.3.2"
append-transform@^0.4.0:
version "0.4.0"
@ -1125,19 +1182,6 @@ buble@^0.19.3:
os-homedir "^1.0.1"
vlq "^1.0.0"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-crc32@^0.2.1:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@ -1146,11 +1190,6 @@ buffer-equal@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
@ -1175,75 +1214,33 @@ buffers@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
builder-util-runtime@6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-6.1.0.tgz#85f0c7bddbe4950ad708a1455b6ba79e16f2b731"
integrity sha512-/1dvNkUNSlMQuIEMBGzJUS60tmDBBA6CYiWT5P9ZTIl2nskMX8VdEClTNTfknkCBQqZArgSTXfWrNmcbXEkbEg==
builder-util-runtime@8.2.1, builder-util-runtime@^8.2.1:
version "8.2.1"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.2.1.tgz#2d16be6cb040b3654d84b76487256158e60b6e3b"
integrity sha512-2TkeTcI9bDlK5azRZSJJNxhAgW1DK+JY3jHK0UWPxgJcan4GZSVDNNO3sXntNxrp+JAdPHMF14rzNd/G53lvqw==
dependencies:
bluebird-lst "^1.0.6"
debug "^4.1.0"
fs-extra-p "^7.0.0"
bluebird-lst "^1.0.7"
debug "^4.1.1"
fs-extra-p "^7.0.1"
sax "^1.2.4"
builder-util-runtime@7.1.0, builder-util-runtime@^7.0.0, builder-util-runtime@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-7.1.0.tgz#f0bf1f247d0eb79ea8a20d0c9178ffa0daf98a80"
integrity sha512-TAsx651+q6bXYry21SzQblYQBUlfu4ixbDa6k2Nvts+kHO9ajyr0gDuHJsamxBaAyUUi5EldPABqsFERDEK3Hg==
dependencies:
bluebird-lst "^1.0.6"
debug "^4.1.0"
fs-extra-p "^7.0.0"
sax "^1.2.4"
builder-util-runtime@~7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-7.0.0.tgz#becbcaf1916ce4f6d6c18ddc55cecd6e5f7fd7ed"
integrity sha512-T31IWtU82sk6eNvYPZksxk49yM9OtJN2fsJ1XQXFKRP85twNE1KTDFEtnVcRLHy13M++1RsDiAZPWU1O6EabQA==
dependencies:
bluebird-lst "^1.0.6"
debug "^4.1.0"
fs-extra-p "^7.0.0"
sax "^1.2.4"
builder-util@9.0.0, builder-util@~9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-9.0.0.tgz#e8c4c55c1e316707c9918606a11714e2a16aa0f2"
integrity sha512-uiaRwYJakfYrZRzRYWnXEnhHNOoWHvfJcobaQsq/vcGwptG9WGFl53II7YlisTkpJUATNoyblZOv2blKq1r8SQ==
builder-util@9.7.1, builder-util@~9.7.1:
version "9.7.1"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-9.7.1.tgz#31234b48ac574eb1cd4b4eaa982c0480571eae7a"
integrity sha512-txpzYIeuHFjrOQWPTJDvhJYisIVGJdSG9ppccE+y7agT0YNhBlVHGnd8+HgFTajYE34xzB5zf1/zxiiDqSKSpA==
dependencies:
"7zip-bin" "~4.1.0"
app-builder-bin "2.4.1"
bluebird-lst "^1.0.6"
builder-util-runtime "^7.0.0"
chalk "^2.4.1"
debug "^4.1.0"
fs-extra-p "^7.0.0"
is-ci "^1.2.1"
js-yaml "^3.12.0"
lazy-val "^1.0.3"
semver "^5.6.0"
source-map-support "^0.5.9"
stat-mode "^0.2.2"
temp-file "^3.2.0"
builder-util@9.1.0, builder-util@~9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-9.1.0.tgz#95607fddf56cc6cb65e3c5c0c6265eb476c12ecf"
integrity sha512-YbgEQDPIuIiVzMr5yqC39WQCNjVsE0Vs6aO2bx8X2Han5zwJhUMiPiIQeYxmHldILEwiccNZs+Lb6SgkvbtHoQ==
dependencies:
"7zip-bin" "~4.1.0"
app-builder-bin "2.4.1"
bluebird-lst "^1.0.6"
builder-util-runtime "^7.1.0"
chalk "^2.4.1"
debug "^4.1.0"
fs-extra-p "^7.0.0"
is-ci "^1.2.1"
js-yaml "^3.12.0"
lazy-val "^1.0.3"
semver "^5.6.0"
source-map-support "^0.5.9"
stat-mode "^0.2.2"
temp-file "^3.2.0"
app-builder-bin "2.6.6"
bluebird-lst "^1.0.7"
builder-util-runtime "^8.2.1"
chalk "^2.4.2"
debug "^4.1.1"
fs-extra-p "^7.0.1"
is-ci "^2.0.0"
js-yaml "^3.13.0"
source-map-support "^0.5.11"
stat-mode "^0.3.0"
temp-file "^3.3.2"
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
@ -1442,9 +1439,10 @@ chalk@^2.3.1, chalk@^2.3.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
@ -1515,10 +1513,10 @@ ci-info@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534"
ci-info@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
@ -2171,7 +2169,7 @@ cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
dashdash@^1.12.0:
dashdash@1.14.1, dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
@ -2218,7 +2216,7 @@ debug@^2.1.3, debug@^2.2.0, debug@^2.6.8:
dependencies:
ms "2.0.0"
debug@^4.1.0:
debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@ -2446,17 +2444,17 @@ dir-glob@^2.0.0:
arrify "^1.0.1"
path-type "^3.0.0"
dmg-builder@6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-6.2.0.tgz#681d4624a1fe0b517142d597aa4330b4eb697f8b"
integrity sha512-tyChjVJ/XH2eOtIp5VWC1cngxQ7KZJDQivyoo3nS6LAAivZhuCdnZa3IgyAD2XXF4xWhzhu3lZa6KE/LhlFf9Q==
dmg-builder@6.6.1:
version "6.6.1"
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-6.6.1.tgz#d1761e36e624ebe3f6c57a8c4d728b95b51fb255"
integrity sha512-aIbpQG3es+gHTFtsBQE4fmSYVM60yewxJZsN6FhkAmAmNaoO45bEQNJZsRX0YE49+imiSC92mJmFAEP6iKE0Tg==
dependencies:
app-builder-lib "~20.33.0"
bluebird-lst "^1.0.6"
builder-util "~9.0.0"
fs-extra-p "^7.0.0"
app-builder-lib "~20.40.0"
bluebird-lst "^1.0.7"
builder-util "~9.7.1"
fs-extra-p "^7.0.1"
iconv-lite "^0.4.24"
js-yaml "^3.12.0"
js-yaml "^3.13.0"
parse-color "^1.0.0"
sanitize-filename "^1.6.1"
@ -2521,7 +2519,7 @@ dotenv-expand@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275"
dotenv@^6.1.0:
dotenv@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
@ -2568,24 +2566,24 @@ ejs@~2.5.6:
version "2.5.7"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
electron-builder@20.33.1:
version "20.33.1"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.33.1.tgz#02729a5773004948a734de471e1623d020c478f9"
integrity sha512-SV5fCusPqNtxXpgYTLLcmTh2bFMOVTjb7ErzV3C2vUlpMzI3Dw/JF5Cwm08Si000gfby5/UP+JRjul5KvZlJoQ==
electron-builder@20.40.0:
version "20.40.0"
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-20.40.0.tgz#8959e5dc3bfd4410b07770da11283b92b8237157"
integrity sha512-xTUq9lMdqXzc+2h68l0MtESk6cErNye7n0zXKKUlBvun+KYiIzhzpIJyt1OW2Wb7K89KHP1aGuxLXQe8h/LQyg==
dependencies:
app-builder-lib "20.33.1"
bluebird-lst "^1.0.6"
builder-util "9.0.0"
builder-util-runtime "6.1.0"
chalk "^2.4.1"
dmg-builder "6.2.0"
fs-extra-p "^7.0.0"
is-ci "^1.2.1"
lazy-val "^1.0.3"
read-config-file "3.2.0"
app-builder-lib "20.40.0"
bluebird-lst "^1.0.7"
builder-util "9.7.1"
builder-util-runtime "8.2.1"
chalk "^2.4.2"
dmg-builder "6.6.1"
fs-extra-p "^7.0.1"
is-ci "^2.0.0"
lazy-val "^1.0.4"
read-config-file "3.2.2"
sanitize-filename "^1.6.1"
update-notifier "^2.5.0"
yargs "^12.0.2"
yargs "^13.2.2"
electron-chromedriver@~3.0.0:
version "3.0.0"
@ -2629,7 +2627,7 @@ electron-icon-maker@0.0.3:
icon-gen "^1.0.7"
jimp "^0.2.27"
electron-is-dev@0.3.0, electron-is-dev@^0.3.0:
electron-is-dev@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-0.3.0.tgz#14e6fda5c68e9e4ecbeff9ccf037cbd7c05c5afe"
integrity sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4=
@ -2646,52 +2644,23 @@ electron-osx-sign@0.4.11:
minimist "^1.2.0"
plist "^3.0.1"
electron-publish@20.33.0:
version "20.33.0"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.33.0.tgz#cf845d86f1199f18d47b920a4153d05396c4a3b4"
integrity sha512-zz2BriQPpox04eoRXe2R6AYhgHkz7z4jPv/OnODCqeeTuBQ1oYaD0ygC7JTjigd0SnqfB+tdp7PZo4l4kY1V8A==
electron-publish@20.40.0:
version "20.40.0"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.40.0.tgz#156eacd9b8bab563a3f8723e9b45214915a9681d"
integrity sha512-mkjtsIgftRszuT/8do8TszmddokDnu254OyTeL8nE780o/A8t68oXHZzvlTJ4AQ8uBOYrA87JDO/BFCWjnVArA==
dependencies:
bluebird-lst "^1.0.6"
builder-util "~9.0.0"
builder-util-runtime "^7.0.0"
chalk "^2.4.1"
fs-extra-p "^7.0.0"
lazy-val "^1.0.3"
mime "^2.3.1"
electron-publish@20.33.2:
version "20.33.2"
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-20.33.2.tgz#7f78454bdf8747b8e4a23eab7ef596840a98853a"
integrity sha512-9LeawWk3Ve6goP8UxQEEj/YD5oXB8gWsVDb0PiPmm5kNP6O1So7h+nKiKaX1cZF9gsI7iRcmJ3soSuLXGU7GKg==
dependencies:
bluebird-lst "^1.0.6"
builder-util "~9.1.0"
builder-util-runtime "^7.1.0"
chalk "^2.4.1"
fs-extra-p "^7.0.0"
lazy-val "^1.0.3"
mime "^2.3.1"
bluebird-lst "^1.0.7"
builder-util "~9.7.1"
builder-util-runtime "^8.2.1"
chalk "^2.4.2"
fs-extra-p "^7.0.1"
lazy-val "^1.0.4"
mime "^2.4.1"
electron-to-chromium@^1.2.7:
version "1.3.41"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.41.tgz#7e33643e00cd85edfd17e04194f6d00e73737235"
electron-updater@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-3.2.0.tgz#f62c780f9142cc6683074fcf8c1de02da4b86c6a"
integrity sha512-gWs8UsQszhkZrNYMm8Yx6lIU0w4666YzGsBZncm8wLTKJiKPZoC1qElRxFHafqr8+fOscv9+ntrgQrI+nQ63vw==
dependencies:
bluebird-lst "^1.0.6"
builder-util-runtime "~7.0.0"
electron-is-dev "^0.3.0"
fs-extra-p "^7.0.0"
js-yaml "^3.12.0"
lazy-val "^1.0.3"
lodash.isequal "^4.5.0"
pako "^1.0.6"
semver "^5.6.0"
source-map-support "^0.5.9"
electron@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/electron/-/electron-4.1.2.tgz#dc8be0f219c73d60a97675d6d3c5b040c4f50513"
@ -2738,6 +2707,11 @@ emoji-js@3.4.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e"
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@ -3491,7 +3465,7 @@ from2@^2.1.0, from2@^2.1.1:
inherits "^2.0.1"
readable-stream "^2.0.0"
fs-extra-p@^7.0.0:
fs-extra-p@^7.0.0, fs-extra-p@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fs-extra-p/-/fs-extra-p-7.0.1.tgz#4eec0b6dfa150fa90f6ddd773b4fb1d55cad54e3"
integrity sha512-yhd2OV0HnHt2oitlp+X9hl2ReX4X/7kQeL7/72qzPHTZj5eUPGzAKOvEglU02Fa1OeG2rSy/aKB4WGVaLiF8tw==
@ -3629,6 +3603,11 @@ get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-func-name@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
@ -4592,12 +4571,12 @@ is-ci@^1.0.10:
dependencies:
ci-info "^1.0.0"
is-ci@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==
is-ci@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
dependencies:
ci-info "^1.5.0"
ci-info "^2.0.0"
is-data-descriptor@^0.1.4:
version "0.1.4"
@ -4871,12 +4850,10 @@ isbinaryfile@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
isbinaryfile@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80"
integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==
dependencies:
buffer-alloc "^1.2.0"
isbinaryfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.0.tgz#07d1061c21598b41292b0f5c68add5eab601ad8e"
integrity sha512-RBtmso6l2mCaEsUvXngMTIjg3oheXo0MgYzzfT6sk44RYggPnm9fT+cQJAmzRnJIxPHXg9FZglqDJGW28dvcqA==
isexe@^2.0.0:
version "2.0.0"
@ -5013,10 +4990,10 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.12.0:
version "3.12.2"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc"
integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==
js-yaml@3.13.0, js-yaml@^3.12.1, js-yaml@^3.13.0:
version "3.13.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.0.tgz#38ee7178ac0eea2c97ff6d96fff4b18c7d8cf98e"
integrity sha512-pZZoSxcCYco+DIKBTimr67J6Hy+EYGZDY/HCWC+iAEA9h1ByhMXAIVUXMcMFpOCxQ/xjXmPI2MkDL5HRm5eFrQ==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
@ -5231,9 +5208,10 @@ lazy-cache@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
lazy-val@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
lazy-val@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65"
integrity sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q==
lazystream@^1.0.0:
version "1.0.0"
@ -5387,10 +5365,6 @@ lodash.isempty@^4.1.2:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
lodash.isfunction@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.8.tgz#4db709fc81bc4a8fd7127a458a5346c5cdce2c6b"
@ -5742,9 +5716,10 @@ mime@^1.5.0:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mime@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
mime@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.1.tgz#19eb7357bebbda37df585b14038347721558c715"
integrity sha512-VRUfmQO0rCd3hKwBymAn3kxYzBHr3I/wdVMywgG3HhXOwrCQgN84ZagpdTm2tZ4TNtwsSmyJWYO88mb5XvzGqQ==
mimic-fn@^1.0.0:
version "1.1.0"
@ -6165,7 +6140,7 @@ nopt@~1.0.10:
dependencies:
abbrev "1"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.4.0:
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
dependencies:
@ -6174,6 +6149,16 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
dependencies:
hosted-git-info "^2.1.4"
resolve "^1.10.0"
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
@ -6440,7 +6425,7 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"
os-locale@^3.0.0:
os-locale@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
@ -6571,11 +6556,6 @@ package-json@^4.0.0:
registry-url "^3.0.3"
semver "^5.1.0"
pako@^1.0.6:
version "1.0.10"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732"
integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==
pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
@ -6702,6 +6682,11 @@ path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
@ -7645,20 +7630,20 @@ read-chunk@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-1.0.1.tgz#5f68cab307e663f19993527d9b589cace4661194"
read-config-file@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-3.2.0.tgz#50a2756a9a128ab9dcbe087e2724c512e3d0ccd1"
integrity sha512-i1QRc5jy4sHm9YBGb6ArA5SU1mDrc5wu2mnm3r9gPnm+LVZhBGbpTCKqAXyvV4TJHnBR3Yaaww+9b3DyRZcfww==
read-config-file@3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-3.2.2.tgz#57bbff7dd97caf237d0d625bd541c6d0efb4d067"
integrity sha512-PuFpMgZF01VB0ydH1dfitAxCP/fh+qnfbA9cYNIPoxPbz0SMngsrafCtaHDWfER7MwlDz4fmrNBhPkakxxFpTg==
dependencies:
ajv "^6.5.5"
ajv-keywords "^3.2.0"
bluebird-lst "^1.0.6"
dotenv "^6.1.0"
ajv "^6.9.2"
ajv-keywords "^3.4.0"
bluebird-lst "^1.0.7"
dotenv "^6.2.0"
dotenv-expand "^4.2.0"
fs-extra-p "^7.0.0"
js-yaml "^3.12.0"
fs-extra-p "^7.0.1"
js-yaml "^3.12.1"
json5 "^2.1.0"
lazy-val "^1.0.3"
lazy-val "^1.0.4"
read-last-lines@1.3.0:
version "1.3.0"
@ -8089,6 +8074,11 @@ require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
require-uncached@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3"
@ -8134,6 +8124,13 @@ resolve-url@^0.2.1, resolve-url@~0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
resolve@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
dependencies:
path-parse "^1.0.6"
resolve@^1.2.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
@ -8322,10 +8319,10 @@ semver@^5.0.1, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
semver@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
semver@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65"
integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==
semver@~5.3.0:
version "5.3.0"
@ -8589,7 +8586,7 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-support@^0.5.9:
source-map-support@^0.5.11:
version "0.5.11"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.11.tgz#efac2ce0800355d026326a0ca23e162aeac9a4e2"
integrity sha512-//sajEx/fGL3iw6fltKMdPvy8kL3kJ2O3iuYlRoT3k9Kb4BjOoZ+BZzaNHeuaruSt+Kf3Zk9tnfAQg9/AJqUVQ==
@ -8742,9 +8739,10 @@ ssri@^5.2.4:
dependencies:
safe-buffer "^5.1.1"
stat-mode@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.2.2.tgz#e6c80b623123d7d80cf132ce538f346289072502"
stat-mode@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-0.3.0.tgz#69283b081f851582b328d2a4ace5f591ce52f54b"
integrity sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==
state-toggle@^1.0.0:
version "1.0.0"
@ -8845,6 +8843,15 @@ string-width@^2.1.0, string-width@^2.1.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
string-width@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
dependencies:
emoji-regex "^7.0.1"
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string_decoder@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@ -8895,6 +8902,13 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
dependencies:
ansi-regex "^4.1.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@ -9051,7 +9065,7 @@ tar@^4:
safe-buffer "^5.1.2"
yallist "^3.0.2"
temp-file@^3.2.0:
temp-file@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.2.tgz#69b6daf1bbe23231d0a5d03844e3d96f3f531aaa"
integrity sha512-FGKccAW0Mux9hC/2bdUIe4bJRv4OyVo4RpVcuplFird1V/YoplIFbnPZjfzbJSf/qNvRZIRB9/4n/RkI0GziuQ==
@ -10058,7 +10072,7 @@ y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0:
y18n@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
@ -10075,10 +10089,10 @@ yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
yargs-parser@^11.1.1:
version "11.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"
integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==
yargs-parser@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b"
integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
@ -10137,23 +10151,22 @@ yargs@^10.0.3:
y18n "^3.2.1"
yargs-parser "^8.0.0"
yargs@^12.0.2:
version "12.0.5"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
yargs@^13.2.2:
version "13.2.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993"
integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==
dependencies:
cliui "^4.0.0"
decamelize "^1.2.0"
find-up "^3.0.0"
get-caller-file "^1.0.1"
os-locale "^3.0.0"
get-caller-file "^2.0.1"
os-locale "^3.1.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^2.0.0"
string-width "^3.0.0"
which-module "^2.0.0"
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^11.1.1"
y18n "^4.0.0"
yargs-parser "^13.0.0"
yargs@^7.0.0:
version "7.1.0"