diff --git a/.gitignore b/.gitignore index 1bea176d7..d82b0f3a3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules /renderer/.next /app/dist /dist +/dist-js diff --git a/main/common/aperture.js b/main/aperture.ts similarity index 56% rename from main/common/aperture.js rename to main/aperture.ts index 61563ac24..8221402fc 100644 --- a/main/common/aperture.js +++ b/main/aperture.ts @@ -1,63 +1,53 @@ -'use strict'; +import {windowManager} from './windows/manager'; +import {setRecordingTray, disableTray, resetTray} from './tray'; +import {setCropperShortcutAction} from './global-accelerators'; +import {settings} from './common/settings'; +import {track} from './common/analytics'; +import {plugins} from './plugins'; +import {getAudioDevices, getSelectedInputDeviceId} from './utils/devices'; +import {showError} from './utils/errors'; +import {RecordServiceContext, RecordServiceState} from './plugins/service-context'; +import {setCurrentRecording, updatePluginState, stopCurrentRecording} from './recording-history'; +import {Recording} from './video'; +import {ApertureOptions, StartRecordingOptions} from './common/types'; +import {InstalledPlugin} from './plugins/plugin'; +import {RecordService, RecordServiceHook} from './plugins/service'; const createAperture = require('aperture'); - -const {openEditorWindow} = require('../editor'); -const {closePrefsWindow} = require('../preferences'); -const {setRecordingTray, disableTray, resetTray} = require('../tray'); -const {disableCroppers, setRecordingCroppers, closeAllCroppers} = require('../cropper'); -const {setCropperShortcutAction} = require('../global-accelerators'); - -// eslint-disable-next-line no-unused-vars -const {convertToH264} = require('../utils/encoding'); - -const settings = require('./settings'); -const {track} = require('./analytics'); -const plugins = require('./plugins'); -const {getAudioDevices} = require('../utils/devices'); -const {showError} = require('../utils/errors'); -const {RecordServiceContext} = require('../service-context'); -const {setCurrentRecording, updatePluginState, stopCurrentRecording} = require('../recording-history'); - const aperture = createAperture(); -const {videoCodecs} = createAperture; - -// eslint-disable-next-line no-unused-vars -const recordHevc = videoCodecs.has('hevc'); -let lastUsedSettings; -let recordingPlugins = []; -const serviceState = new Map(); -let apertureOptions; -let recordingName; -let past; +let recordingPlugins: Array<{plugin: InstalledPlugin; service: RecordService}> = []; +const serviceState = new Map(); +let apertureOptions: ApertureOptions; +let recordingName: string | undefined; +let past: number | undefined; -const setRecordingName = name => { +const setRecordingName = (name: string) => { recordingName = name; }; const serializeEditPluginState = () => { - const result = {}; + const result: Record | undefined>> = {}; for (const {plugin, service} of recordingPlugins) { if (!result[plugin.name]) { result[plugin.name] = {}; } - result[plugin.name][service.title] = serviceState.get(service.title).persistedState; + result[plugin.name][service.title] = serviceState.get(service.title)?.persistedState; } return result; }; -const callPlugins = async method => Promise.all(recordingPlugins.map(async ({plugin, service}) => { +const callPlugins = async (method: RecordServiceHook) => Promise.all(recordingPlugins.map(async ({plugin, service}) => { if (service[method] && typeof service[method] === 'function') { try { - await service[method]( + await service[method]?.( new RecordServiceContext({ + plugin, apertureOptions, - state: serviceState.get(service.title), - config: plugin.config, + state: serviceState.get(service.title) ?? {}, setRecordingName }) ); @@ -68,7 +58,7 @@ const callPlugins = async method => Promise.all(recordingPlugins.map(async ({plu })); const cleanup = async () => { - closeAllCroppers(); + windowManager.cropper?.close(); resetTray(); await callPlugins('didStopRecording'); @@ -77,7 +67,7 @@ const cleanup = async () => { setCropperShortcutAction(); }; -const startRecording = async options => { +export const startRecording = async (options: StartRecordingOptions) => { if (past) { return; } @@ -85,9 +75,9 @@ const startRecording = async options => { past = Date.now(); recordingName = undefined; - closePrefsWindow(); + windowManager.preferences?.close(); + windowManager.cropper?.disable(); disableTray(); - disableCroppers(); const {cropperBounds, screenBounds, displayId} = options; @@ -108,19 +98,15 @@ const startRecording = async options => { screenId: displayId }; - lastUsedSettings = { - recordedFps: apertureOptions.fps - }; - - if (recordAudio === true) { + if (recordAudio) { // In case for some reason the default audio device is not set // use the first available device for recording - const audioInputDeviceId = settings.getSelectedInputDeviceId(); + const audioInputDeviceId = getSelectedInputDeviceId(); if (audioInputDeviceId) { apertureOptions.audioDeviceId = audioInputDeviceId; } else { const [defaultAudioDevice] = await getAudioDevices(); - apertureOptions.audioDeviceId = defaultAudioDevice && defaultAudioDevice.id; + apertureOptions.audioDeviceId = defaultAudioDevice?.id; } } @@ -132,12 +118,15 @@ const startRecording = async options => { console.log(`Collected settings after ${(Date.now() - past) / 1000}s`); recordingPlugins = plugins - .getRecordingPlugins() + .recordingPlugins .flatMap( - plugin => plugin.recordServicesWithStatus - // Make sure service is valid and enabled - .filter(({title, isEnabled}) => isEnabled && plugin.config.validServices.includes(title)) - .map(service => ({plugin, service})) + plugin => { + const validServices = plugin.config.validServices; + return plugin.recordServicesWithStatus + // Make sure service is valid and enabled + .filter(({title, isEnabled}) => isEnabled && validServices.includes(title)) + .map(service => ({plugin, service})); + } ); for (const {service, plugin} of recordingPlugins) { @@ -154,12 +143,12 @@ const startRecording = async options => { filePath, name: recordingName, apertureOptions, - editPlugins: serializeEditPluginState() + plugins: serializeEditPluginState() }); } catch (error) { track('recording/stopped/error'); - showError(error, {title: 'Recording error'}); - past = null; + showError(error, {title: 'Recording error', plugin: undefined}); + past = undefined; cleanup(); return; } @@ -172,18 +161,18 @@ const startRecording = async options => { } console.log(`Started recording after ${startTime}s`); - setRecordingCroppers(); + windowManager.cropper?.setRecording(); setRecordingTray(stopRecording); setCropperShortcutAction(stopRecording); past = Date.now(); // Track aperture errors after recording has started, to avoid kap freezing if something goes wrong - aperture.recorder.catch(error => { + aperture.recorder.catch((error: any) => { // Make sure it doesn't catch the error of ending the recording if (past) { track('recording/stopped/error'); - showError(error, {title: 'Recording error'}); - past = null; + showError(error, {title: 'Recording error', plugin: undefined}); + past = undefined; cleanup(); } }); @@ -192,14 +181,14 @@ const startRecording = async options => { updatePluginState(serializeEditPluginState()); }; -const stopRecording = async () => { +export const stopRecording = async () => { // Ensure we only stop recording once if (!past) { return; } console.log(`Stopped recording after ${(Date.now() - past) / 1000}s`); - past = null; + past = undefined; let filePath; @@ -207,31 +196,23 @@ const stopRecording = async () => { filePath = await aperture.stopRecording(); } catch (error) { track('recording/stopped/error'); - showError(error, {title: 'Recording error'}); + showError(error, {title: 'Recording error', plugin: undefined}); cleanup(); return; } - const {recordedFps} = lastUsedSettings; - try { cleanup(); } finally { track('editor/opened/recording'); - // TODO: bring this back when we figure out how to convert hevc files - // if (recordHevc) { - // openEditorWindow(await convertToH264(filePath), {recordedFps, isNewRecording: true, originalFilePath: filePath}); - // } else { - await openEditorWindow(filePath, {recordedFps, isNewRecording: true, recordingName}); - // } + const recording = new Recording({ + filePath, + title: recordingName, + apertureOptions + }); + await recording.openEditorWindow(); stopCurrentRecording(recordingName); } }; - -module.exports = { - startRecording, - stopRecording, - getAudioDevices -}; diff --git a/main/utils/accelerator-validator.js b/main/common/accelerator-validator.ts similarity index 82% rename from main/utils/accelerator-validator.js rename to main/common/accelerator-validator.ts index 4ebca4b12..5d59bef2f 100644 --- a/main/utils/accelerator-validator.js +++ b/main/common/accelerator-validator.ts @@ -1,5 +1,3 @@ -'use strict'; - // The goal of this file is validating accelerator values we receive from the user // to make sure that they are can be used with the electron api https://www.electronjs.org/docs/api/accelerator @@ -46,9 +44,9 @@ const codes = [ 'numsub', 'nummult', 'numdiv' -]; +] as const; -const keyCodeRegex = new RegExp('^([\\dA-Z~`!@#$%^&*()_+=.,<>?;:\'"\\-\\/\\\\\\[\\]\\{\\}\\|]|F([1-9]|1[\\d]|2[0-4])|' + codes.join('|') + ')$'); +const getKeyCodeRegex = () => new RegExp('^([\\dA-Z~`!@#$%^&*()_+=.,<>?;:\'"\\-\\/\\\\\\[\\]\\{\\}\\|]|F([1-9]|1[\\d]|2[0-4])|' + codes.join('|') + ')$'); const shiftKeyMap = new Map([ ['~', '`'], @@ -102,7 +100,7 @@ const namedKeyCodeMap = new Map([ ['Clear', 'Numlock'] ]); -const checkAccelerator = accelerator => { +export const checkAccelerator = (accelerator: string) => { if (!accelerator) { return true; } @@ -113,7 +111,7 @@ const checkAccelerator = accelerator => { return false; } - if (!keyCodeRegex.test(parts[parts.length - 1])) { + if (!getKeyCodeRegex().test(parts[parts.length - 1])) { return false; } @@ -121,15 +119,10 @@ const checkAccelerator = accelerator => { return metaKeys.every(part => modifiers.includes(part)) && metaKeys.some(part => part !== 'Shift'); }; -const eventKeyToAccelerator = (key, location) => { +export const eventKeyToAccelerator = (key: string, location: number) => { if (location === 3) { return numpadKeyMap.get(key); } - return namedKeyCodeMap.get(key) || shiftKeyMap.get(key) || key.toUpperCase(); -}; - -module.exports = { - checkAccelerator, - eventKeyToAccelerator + return namedKeyCodeMap.get(key) ?? shiftKeyMap.get(key) ?? key.toUpperCase(); }; diff --git a/main/common/analytics.js b/main/common/analytics.ts similarity index 56% rename from main/common/analytics.js rename to main/common/analytics.ts index 9ffbdd5e2..4b2f948ba 100644 --- a/main/common/analytics.js +++ b/main/common/analytics.ts @@ -1,25 +1,26 @@ 'use strict'; -const util = require('electron-util'); +import util from 'electron-util'; +import {parse} from 'semver'; +import {settings} from './settings'; + const Insight = require('insight'); -const {parse} = require('semver'); const pkg = require('../../package'); -const settings = require('./settings'); const trackingCode = 'UA-84705099-2'; const insight = new Insight({trackingCode, pkg}); const version = parse(pkg.version); -const track = (...paths) => { +export const track = (...paths: string[]) => { const allowAnalytics = settings.get('allowAnalytics'); if (allowAnalytics) { - console.log('Tracking', `v${version.major}.${version.minor}`, ...paths); - insight.track(`v${version.major}.${version.minor}`, ...paths); + console.log('Tracking', `v${version?.major}.${version?.minor}`, ...paths); + insight.track(`v${version?.major}.${version?.minor}`, ...paths); } }; -const initializeAnalytics = () => { +export const initializeAnalytics = () => { if (util.isFirstAppLaunch()) { insight.track('install'); } @@ -29,8 +30,3 @@ const initializeAnalytics = () => { settings.set('version', pkg.version); } }; - -module.exports = { - initializeAnalytics, - track -}; diff --git a/main/common/constants.js b/main/common/constants.js deleted file mode 100644 index 2cd79833f..000000000 --- a/main/common/constants.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -const supportedVideoExtensions = ['mp4', 'mov', 'm4v']; - -const formatExtensions = new Map([ - ['av1', 'mp4'] -]); - -const getFormatExtension = format => formatExtensions.get(format) || format; - -module.exports = { - supportedVideoExtensions, - getFormatExtension, - defaultInputDeviceId: 'SYSTEM_DEFAULT' -}; diff --git a/main/common/constants.ts b/main/common/constants.ts new file mode 100644 index 000000000..876d2ac7d --- /dev/null +++ b/main/common/constants.ts @@ -0,0 +1,14 @@ +import {Format} from './types'; + +export const supportedVideoExtensions = ['mp4', 'mov', 'm4v']; + +const formatExtensions = new Map([ + ['av1', 'mp4'] +]); + +export const formats = [Format.mp4, Format.av1, Format.gif, Format.apng, Format.webm]; + +export const getFormatExtension = (format: Format) => formatExtensions.get(format) ?? format; + +export const defaultInputDeviceId = 'SYSTEM_DEFAULT'; + diff --git a/main/common/flags.ts b/main/common/flags.ts new file mode 100644 index 000000000..0fb0ade76 --- /dev/null +++ b/main/common/flags.ts @@ -0,0 +1,10 @@ +import Store from 'electron-store'; + +export const flags = new Store<{ + backgroundEditorConversion: boolean; +}>({ + name: 'flags', + defaults: { + backgroundEditorConversion: false + } +}); diff --git a/main/common/logger.js b/main/common/logger.js deleted file mode 100644 index 66c3695ed..000000000 --- a/main/common/logger.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -let window; -let windowIsReady = false; -let pendingMessages = []; - -function init(mainWindow) { - window = mainWindow; - - window.on('show', () => { - if (windowIsReady === false) { - windowIsReady = true; - for (const chunk of pendingMessages) { - window.webContents.send('log', chunk); - } - - pendingMessages = []; - } - }); -} - -function log(...msgs) { - if (process.type === 'browser') { // Main process - if (window && windowIsReady) { - window.webContents.send('log', msgs); - } else { - pendingMessages.push(msgs); - } - } else { - console.log(...msgs); - } -} - -exports.init = init; -exports.log = log; diff --git a/main/common/notifications.js b/main/common/notifications.js deleted file mode 100644 index 9136585bb..000000000 --- a/main/common/notifications.js +++ /dev/null @@ -1,16 +0,0 @@ -const electron = require('electron'); - -const {app, Notification} = electron; - -const notify = text => { - const notification = new Notification({ - title: app.name, - body: text - }); - - notification.show(); -}; - -module.exports = { - notify -}; diff --git a/main/common/plugins.js b/main/common/plugins.js deleted file mode 100644 index 1b859f9c9..000000000 --- a/main/common/plugins.js +++ /dev/null @@ -1,304 +0,0 @@ -'use strict'; - -const path = require('path'); -const fs = require('fs'); -const electron = require('electron'); -const got = require('got'); -const execa = require('execa'); -const makeDir = require('make-dir'); -const packageJson = require('package-json'); - -const {app, Notification} = electron; - -const {refreshRecordPluginItems} = require('../menus'); -const {openConfigWindow} = require('../config'); -const {openPrefsWindow} = require('../preferences'); -const {notify} = require('./notifications'); -const {track} = require('./analytics'); -const {InstalledPlugin, NpmPlugin, recordPluginServiceState} = require('../plugin'); -const {showError} = require('../utils/errors'); - -// Need to persist the notification, otherwise it is garbage collected and the actions don't trigger -// https://github.com/electron/electron/issues/12690 -let pluginNotification; - -class Plugins { - constructor() { - this.yarnBin = path.join(__dirname, '../../node_modules/yarn/bin/yarn.js'); - this._makePluginsDir(); - this.appVersion = app.getVersion(); - } - - setUpdateExportOptions(updateExportOptions) { - this.updateExportOptions = updateExportOptions; - } - - async enableService(service, plugin) { - const wasEnabled = recordPluginServiceState.get(service.title) || false; - - if (wasEnabled) { - recordPluginServiceState.set(service.title, false); - return this.refreshRecordPluginServices(); - } - - if (!plugin.config.validServices.includes(service.title)) { - openPrefsWindow({target: {name: plugin.name, action: 'configure'}}); - return; - } - - if (service.willEnable) { - try { - const canEnable = await service.willEnable(); - - if (canEnable) { - recordPluginServiceState.set(service.title, true); - } - } catch (error) { - showError(error, {title: `Something went wrong while enabling “${service.title}”`}); - const Sentry = require('./utils/sentry'); - Sentry.captureException(error); - } - - this.refreshRecordPluginServices(); - return; - } - - recordPluginServiceState.set(service.title, true); - this.refreshRecordPluginServices(); - } - - refreshRecordPluginServices = () => { - refreshRecordPluginItems( - this.getRecordingPlugins().flatMap( - plugin => plugin.recordServices.map(service => ({ - ...service, - isEnabled: recordPluginServiceState.get(service.title) || false, - toggleEnabled: () => this.enableService(service, plugin) - })) - ) - ); - } - - _makePluginsDir() { - const cwd = path.join(app.getPath('userData'), 'plugins'); - const fp = path.join(cwd, 'package.json'); - - if (!fs.existsSync(fp)) { - makeDir.sync(cwd); - fs.writeFileSync(fp, '{"dependencies":{}}'); - } - - this.cwd = cwd; - this.pkgPath = fp; - } - - _modifyMainPackageJson(modifier) { - const pkg = JSON.parse(fs.readFileSync(this.pkgPath, 'utf8')); - modifier(pkg); - fs.writeFileSync(this.pkgPath, JSON.stringify(pkg, null, 2)); - } - - async _runYarn(...commands) { - await execa(process.execPath, [this.yarnBin, ...commands], { - cwd: this.cwd, - env: { - ELECTRON_RUN_AS_NODE: 1 - } - }); - } - - _pluginNames() { - const pkg = fs.readFileSync(path.join(this.cwd, 'package.json'), 'utf8'); - return Object.keys(JSON.parse(pkg).dependencies || {}); - } - - async _yarnInstall() { - await this._runYarn('install', '--no-lockfile', '--registry', 'https://registry.npmjs.org'); - } - - async install(name) { - track(`plugin/installed/${name}`); - // We manually add it to the package.json here so we're able to set the version to `latest` - this._modifyMainPackageJson(pkg => { - if (!pkg.dependencies) { - pkg.dependencies = {}; - } - - pkg.dependencies[name] = 'latest'; - }); - - try { - await this._yarnInstall(); - - const plugin = new InstalledPlugin(name); - - if (plugin.plugin.didInstall && typeof plugin.plugin.didInstall === 'function') { - try { - await plugin.plugin.didInstall(plugin.config); - } catch (error) { - showError(error, {plugin}); - } - } - - const {isValid, hasConfig} = plugin; - - const options = (isValid && !hasConfig) ? { - title: 'Plugin installed', - body: `"${plugin.prettyName}" is ready for use` - } : { - title: plugin.isValid ? 'Plugin installed' : 'Configure plugin', - body: `"${plugin.prettyName}" ${plugin.isValid ? 'can be configured' : 'requires configuration'}`, - actions: [ - {type: 'button', text: 'Configure'}, - {type: 'button', text: 'Later'} - ] - }; - - pluginNotification = new Notification(options); - - if (!isValid || hasConfig) { - const openConfig = () => openPrefsWindow({target: {name, action: 'configure'}}); - - pluginNotification.on('click', openConfig); - - pluginNotification.on('action', (_, index) => { - if (index === 0) { - openConfig(); - } else { - pluginNotification.close(); - } - }); - } - - for (const service of plugin.config.validServices) { - if (!service.willEnable) { - recordPluginServiceState.set(service, true); - } - } - - pluginNotification.show(); - this.updateExportOptions(); - this.refreshRecordPluginServices(); - - return plugin; - } catch (error) { - notify(`Something went wrong while installing ${name}`); - this._modifyMainPackageJson(pkg => { - delete pkg.dependencies[name]; - }); - console.log(error); - } - } - - async upgrade() { - await this._yarnInstall(); - } - - async uninstall(name) { - track(`plugin/uninstalled/${name}`); - this._modifyMainPackageJson(pkg => { - delete pkg.dependencies[name]; - }); - const plugin = new InstalledPlugin(name); - - if (plugin.plugin.willUninstall && typeof plugin.plugin.willUninstall === 'function') { - try { - await plugin.plugin.willUninstall(plugin.config); - } catch (error) { - showError(error, {plugin}); - } - } - - plugin.config.clear(); - this.updateExportOptions(); - return new NpmPlugin(plugin.json, { - // Keeping for backwards compatibility - version: plugin.json.kapVersion, - ...plugin.json.kap - }); - } - - async prune() { - await this._yarnInstall(); - } - - getServices(pluginName) { - const { - shareServices = [], - recordServices = [] - } = require(path.join(this.cwd, 'node_modules', pluginName)); - - return [...shareServices, ...recordServices]; - } - - getInstalled() { - try { - return this._pluginNames().map(name => new InstalledPlugin(name)); - } catch (error) { - showError(error); - const Sentry = require('../utils/sentry'); - Sentry.captureException(error); - return []; - } - } - - getSharePlugins() { - return this.getInstalled().filter(plugin => plugin.shareServices.length > 0); - } - - getRecordingPlugins() { - return this.getInstalled().filter(plugin => plugin.recordServices.length > 0); - } - - getEditPlugins() { - return this.getInstalled().filter(plugin => plugin.editServices.length > 0); - } - - getBuiltIn() { - return [{ - pluginPath: './plugins/copy-to-clipboard-plugin', - isCompatible: true, - name: '_copyToClipboard' - }, { - pluginPath: './plugins/save-file-plugin', - isCompatible: true, - name: '_saveToDisk' - }, { - pluginPath: './plugins/open-with-plugin', - isCompatible: true, - name: '_openWith' - }]; - } - - async getFromNpm() { - const url = 'https://api.npms.io/v2/search?q=keywords:kap-plugin+not:deprecated'; - const response = await got(url, {json: true}); - const installed = this._pluginNames(); - - return Promise.all(response.body.results - .map(x => x.package) - .filter(x => x.name.startsWith('kap-')) - .filter(x => !installed.includes(x.name)) // Filter out installed plugins - .map(async x => { - const {kap, kapVersion} = await packageJson(x.name, {fullMetadata: true}); - return new NpmPlugin(x, { - // Keeping for backwards compatibility - version: kapVersion, - ...kap - }); - })); - } - - getPluginService(pluginName, serviceTitle) { - return this.getServices(pluginName).find(shareService => shareService.title === serviceTitle); - } - - async openPluginConfig(name) { - await openConfigWindow(name); - const plugin = new InstalledPlugin(name); - return plugin.isValid; - } -} - -const plugins = new Plugins(); -module.exports = plugins; diff --git a/main/common/settings.js b/main/common/settings.ts similarity index 60% rename from main/common/settings.js rename to main/common/settings.ts index 748dc8bff..c6577520b 100644 --- a/main/common/settings.js +++ b/main/common/settings.ts @@ -1,14 +1,12 @@ 'use strict'; -const {homedir} = require('os'); -const Store = require('electron-store'); +import {homedir} from 'os'; +import Store from 'electron-store'; const {defaultInputDeviceId} = require('./constants'); -const {hasMicrophoneAccess} = require('./system-permissions'); -const {getAudioDevices, getDefaultInputDevice} = require('../utils/devices'); const shortcutToAccelerator = require('../utils/shortcut-to-accelerator'); -const shortcuts = { +export const shortcuts = { triggerCropper: 'Toggle Kap' }; @@ -17,7 +15,32 @@ const shortcutSchema = { default: '' }; -const store = new Store({ +interface Settings { + kapturesDir: string; + allowAnalytics: boolean; + showCursor: boolean; + highlightClicks: boolean; + record60fps: boolean; + loopExports: boolean; + recordKeyboardShortcut: boolean; + recordAudio: boolean; + audioInputDeviceId?: string; + cropperShortcut: { + metaKey: boolean; + altKey: boolean; + ctrlKey: boolean; + shiftKey: boolean; + character: string; + }; + lossyCompression: boolean; + enableShortcuts: boolean; + shortcuts: { + [key in keyof typeof shortcuts]: string + }; + version: string; +} + +export const settings = new Store({ schema: { kapturesDir: { type: 'string', @@ -93,6 +116,7 @@ const store = new Store({ }, shortcuts: { type: 'object', + // eslint-disable-next-line unicorn/no-array-reduce properties: Object.keys(shortcuts).reduce((acc, key) => ({...acc, [key]: shortcutSchema}), {}), default: {} }, @@ -103,43 +127,17 @@ const store = new Store({ } }); -module.exports = store; -module.exports.shortcuts = shortcuts; - // TODO: Remove this when we feel like everyone has migrated -if (store.has('recordKeyboardShortcut')) { - store.set('enableShortcuts', store.get('recordKeyboardShortcut')); - store.delete('recordKeyboardShortcut'); +if (settings.has('recordKeyboardShortcut')) { + settings.set('enableShortcuts', settings.get('recordKeyboardShortcut')); + settings.delete('recordKeyboardShortcut'); } // TODO: Remove this when we feel like everyone has migrated -if (store.has('cropperShortcut')) { - store.set('shortcuts.triggerCropper', shortcutToAccelerator(store.get('cropperShortcut'))); - store.delete('cropperShortcut'); +if (settings.has('cropperShortcut')) { + settings.set('shortcuts.triggerCropper', shortcutToAccelerator(settings.get('cropperShortcut'))); + settings.delete('cropperShortcut'); } -store.set('cropper', {}); -store.set('actionBar', {}); - -const audioInputDeviceId = store.get('audioInputDeviceId'); - -if (hasMicrophoneAccess()) { - (async () => { - const devices = await getAudioDevices(); - - if (!devices.some(device => device.id === audioInputDeviceId)) { - store.set('audioInputDeviceId', defaultInputDeviceId); - } - })(); -} - -module.exports.getSelectedInputDeviceId = () => { - const audioInputDeviceId = store.get('audioInputDeviceId', defaultInputDeviceId); - - if (audioInputDeviceId === defaultInputDeviceId) { - const device = getDefaultInputDevice(); - return device && device.id; - } - - return audioInputDeviceId; -}; +settings.set('cropper' as any, {}); +settings.set('actionBar' as any, {}); diff --git a/main/common/system-permissions.js b/main/common/system-permissions.ts similarity index 73% rename from main/common/system-permissions.js rename to main/common/system-permissions.ts index 1aa407a55..c60f59716 100644 --- a/main/common/system-permissions.js +++ b/main/common/system-permissions.ts @@ -1,10 +1,10 @@ -const {systemPreferences, shell, dialog, app} = require('electron'); +import {systemPreferences, shell, dialog, app} from 'electron'; const {hasScreenCapturePermission, hasPromptedForPermission} = require('mac-screen-capture-permissions'); const {ensureDockIsShowing} = require('../utils/dock'); let isDialogShowing = false; -const promptSystemPreferences = options => async ({hasAsked} = {}) => { +const promptSystemPreferences = (options: {message: string; detail: string; systemPreferencesPath: string}) => async ({hasAsked}: {hasAsked?: boolean} = {}) => { if (hasAsked || isDialogShowing) { return false; } @@ -30,7 +30,7 @@ const promptSystemPreferences = options => async ({hasAsked} = {}) => { return false; }; -const openSystemPreferences = path => shell.openExternal(`x-apple.systempreferences:com.apple.preference.security?${path}`); +export const openSystemPreferences = async (path: string) => shell.openExternal(`x-apple.systempreferences:com.apple.preference.security?${path}`); // Microphone @@ -42,7 +42,7 @@ const microphoneFallback = promptSystemPreferences({ systemPreferencesPath: 'Privacy_Microphone' }); -const ensureMicrophonePermissions = async (fallback = microphoneFallback) => { +export const ensureMicrophonePermissions = async (fallback = microphoneFallback) => { const access = getMicrophoneAccess(); if (access === 'granted') { @@ -62,7 +62,7 @@ const ensureMicrophonePermissions = async (fallback = microphoneFallback) => { return fallback(); }; -const hasMicrophoneAccess = () => getMicrophoneAccess() === 'granted'; +export const hasMicrophoneAccess = () => getMicrophoneAccess() === 'granted'; // Screen Capture (10.15 and newer) @@ -72,7 +72,7 @@ const screenCaptureFallback = promptSystemPreferences({ systemPreferencesPath: 'Privacy_ScreenCapture' }); -const ensureScreenCapturePermissions = (fallback = screenCaptureFallback) => { +export const ensureScreenCapturePermissions = (fallback = screenCaptureFallback) => { const hadAsked = hasPromptedForPermission(); const hasAccess = hasScreenCapturePermission(); @@ -85,12 +85,5 @@ const ensureScreenCapturePermissions = (fallback = screenCaptureFallback) => { return false; }; -const hasScreenCaptureAccess = () => hasScreenCapturePermission(); +export const hasScreenCaptureAccess = () => hasScreenCapturePermission(); -module.exports = { - ensureMicrophonePermissions, - hasMicrophoneAccess, - openSystemPreferences, - ensureScreenCapturePermissions, - hasScreenCaptureAccess -}; diff --git a/main/common/types/base.ts b/main/common/types/base.ts new file mode 100644 index 000000000..692075914 --- /dev/null +++ b/main/common/types/base.ts @@ -0,0 +1,41 @@ +import {Rectangle} from 'electron'; + +export enum Format { + gif = 'gif', + mp4 = 'mp4', + webm = 'webm', + apng = 'apng', + av1 = 'av1' +} + +export enum Encoding { + h264 = 'h264', + hevc = 'hevc', + // eslint-disable-next-line unicorn/prevent-abbreviations + proRes422 = 'proRes422', + // eslint-disable-next-line unicorn/prevent-abbreviations + proRes4444 = 'proRes4444' +} + +export type App = { + url: string; + isDefault: boolean; + icon: string; + name: string; +}; + +export interface ApertureOptions { + fps: number; + cropArea: Rectangle; + showCursor: boolean; + highlightClicks: boolean; + screenId: number; + audioDeviceId?: string; + videoCodec?: Encoding; +} + +export interface StartRecordingOptions { + cropperBounds: Rectangle; + screenBounds: Rectangle; + displayId: number; +} diff --git a/main/common/types/conversion-options.ts b/main/common/types/conversion-options.ts new file mode 100644 index 000000000..e5c3c97c4 --- /dev/null +++ b/main/common/types/conversion-options.ts @@ -0,0 +1,38 @@ +import {App, Format} from './base'; + +export type CreateConversionOptions = { + filePath: string; + options: ConversionOptions; + format: Format; + plugins: { + share: { + pluginName: string; + serviceTitle: string; + app?: App; + }; + }; +}; + +export type EditServiceInfo = { + pluginName: string; + serviceTitle: string; +}; + +export type ConversionOptions = { + startTime: number; + endTime: number; + width: number; + height: number; + fps: number; + shouldCrop: boolean; + shouldMute: boolean; + editService?: EditServiceInfo; +}; + +export enum ConversionStatus { + idle = 'idle', + inProgress = 'inProgress', + failed = 'failed', + canceled = 'canceled', + completed = 'completed' +} diff --git a/main/common/types/index.ts b/main/common/types/index.ts new file mode 100644 index 000000000..e3ab891ef --- /dev/null +++ b/main/common/types/index.ts @@ -0,0 +1,4 @@ +export * from './base'; +export * from './remote-states'; +export * from './conversion-options'; +export * from './window-states'; diff --git a/main/common/types/remote-states.ts b/main/common/types/remote-states.ts new file mode 100644 index 000000000..8617edf0e --- /dev/null +++ b/main/common/types/remote-states.ts @@ -0,0 +1,62 @@ +import {App, Format} from './base'; +import {ConversionStatus} from './conversion-options'; + +export interface ExportOptionsPlugin { + title: string; + pluginName: string; + pluginPath: string; + apps?: App[]; + lastUsed: number; +} + +export type ExportOptionsFormat = { + plugins: ExportOptionsPlugin[]; + format: Format; + prettyFormat: string; + lastUsed: number; +}; + +export type ExportOptionsEditService = { + title: string; + pluginName: string; + pluginPath: string; + hasConfig: boolean; +}; + +export type ExportOptions = { + formats: ExportOptionsFormat[]; + editServices: ExportOptionsEditService[]; + fpsHistory: {[key in Format]: number}; +}; + +export type EditorOptionsRemoteState = (sendUpdate: (state: ExportOptions) => void) => { + actions: { + updatePluginUsage: ({format, plugin}: { + format: Format; + plugin: string; + }) => void; + updateFpsUsage: ({format, fps}: { + format: Format; + fps: number; + }) => void; + }; + getState: () => ExportOptions; +}; + +export interface ConversionState { + title: string; + description: string; + message: string; + progress?: number; + size?: string; + status: ConversionStatus; + canCopy: boolean; +} + +export type ConversionRemoteState = (sendUpdate: (state: ConversionState, id: string) => void) => { + actions: { + copy: (_?: undefined, conversionId?: string) => void; + cancel: (_?: undefined, conversionId?: string) => void; + }; + getState: (conversionId: string) => ConversionState | undefined; +}; diff --git a/main/common/types/window-states.ts b/main/common/types/window-states.ts new file mode 100644 index 000000000..876ddc4fd --- /dev/null +++ b/main/common/types/window-states.ts @@ -0,0 +1,8 @@ + +export interface EditorWindowState { + fps: number; + previewFilePath: string; + filePath: string; + title: string; + conversionId?: string; +} diff --git a/main/conversion.ts b/main/conversion.ts new file mode 100644 index 000000000..2875f8aa9 --- /dev/null +++ b/main/conversion.ts @@ -0,0 +1,337 @@ +import fs from 'fs'; +import {app, clipboard, dialog} from 'electron'; +import {EventEmitter} from 'events'; +import {ConversionOptions, ConversionStatus, CreateConversionOptions, Format} from './common/types'; +import {Video} from './video'; +import {convertTo} from './converters'; +import Export, {ExportOptions} from './export'; +import hash from 'object-hash'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import {plugins} from './plugins'; +import {askForTargetFilePath} from './plugins/built-in/save-file-plugin'; +import path from 'path'; +import {showError} from './utils/errors'; +import {notify} from './utils/notifications'; +import PCancelable from 'p-cancelable'; +import prettyBytes from 'pretty-bytes'; +import {ensureDockIsShowingSync} from './utils/dock'; +import {windowManager} from './windows/manager'; + +const plist = require('plist'); + +// TODO: remove this when exports window is rewritten +const callExportsWindow = (channel: string, data: any) => { + const exportsWindow = windowManager.exports?.get(); + + if (exportsWindow) { + // TODO(karaggeorge): Investigate why `ipc.callRenderer(exportsWindow, channel, data);` is not working here. + ipc.callRenderer(exportsWindow, channel, data); + } +}; + +// A conversion object describes the process of converting a video or recording +// using ffmpeg that can then be shared multiple times using Share plugins +export default class Conversion extends EventEmitter { + static conversionMap = new Map(); + + static get all() { + return [...this.conversionMap.values()]; + } + + id: string; + video: Video; + format: Format; + options: ConversionOptions; + + text = ''; + percentage?: number; + error?: Error; + description: string; + title: string; + finalSize?: string; + convertedFilePath?: string; + requestedFileType?: Format; + + private currentExport?: Export; + private _status: ConversionStatus = ConversionStatus.idle; + + get status() { + return this._status; + } + + set status(newStatus: ConversionStatus) { + this._status = newStatus; + this.emit('updated'); + } + + get canCopy() { + return Boolean(this.convertedFilePath && [Format.gif, Format.apng].includes(this.format)); + } + + private conversionProcess?: PCancelable; + + constructor(video: Video, format: Format, options: ConversionOptions) { + super(); + this.video = video; + this.format = format; + this.options = options; + + this.description = `${this.options.width} x ${this.options.height} at ${this.options.fps} FPS`; + this.title = video.title; + + this.id = hash({ + filePath: video.filePath, + format, + options + }); + + Conversion.conversionMap.set(this.id, this); + + // TODO: remove this when exports window is rewritten + this.on('updated', () => { + callExportsWindow('update-export-data', this.currentExport?.data); + }); + } + + static fromId(id: string) { + return this.conversionMap.get(id); + } + + static getOrCreate(video: Video, format: Format, options: ConversionOptions) { + const id = hash({ + filePath: video.filePath, + format, + options + }); + + return this.fromId(id) ?? new Conversion(video, format, options); + } + + copy = () => { + clipboard.writeBuffer('NSFilenamesPboardType', Buffer.from(plist.build([this.convertedFilePath]))); + notify({ + body: 'The file has been copied to the clipboard', + title: app.name + }); + }; + + async filePathExists() { + if (!this.convertedFilePath) { + return false; + } + + try { + await fs.promises.access(this.convertedFilePath, fs.constants.F_OK); + return true; + } catch { + return false; + } + } + + onProgress = (text: string, progress: number) => { + this.text = text; + this.percentage = Math.max(Math.min(progress, 1), 0); + + if (this.currentExport) { + this.currentExport.text = this.text; + this.currentExport.percentage = this.percentage; + } + + this.emit('updated'); + }; + + filePath = async ({fileType}: {fileType?: Format} = {}) => { + if (fileType) { + this.currentExport?.disableActions(); + } + + if (!this.conversionProcess || (this.requestedFileType !== fileType)) { + this.start(); + } + + this.requestedFileType = fileType; + + try { + this.convertedFilePath = await this.conversionProcess; + this.calculateFileSize(this.convertedFilePath); + return this.convertedFilePath!; + } catch (error) { + // Ensure we re-try the conversion if it fails + this.conversionProcess = undefined; + throw error; + } + }; + + addExport(exportOptions: ExportOptions) { + this.status = ConversionStatus.inProgress; + this.error = undefined; + this.text = 'Initializing…'; + this.percentage = 0; + + const newExport = new Export(this, exportOptions); + + const onProgress = ({text, percentage}: {text: string; percentage: number}) => { + newExport.status = 'processing'; + this.onExportProgress(text, percentage); + }; + + newExport.on('progress', onProgress); + + const cleanup = () => { + this.currentExport = undefined; + newExport.off('progress', onProgress); + }; + + newExport.once('canceled', () => { + newExport.onProgress('Export canceled', 1); + newExport.status = 'canceled'; + this.status = ConversionStatus.canceled; + this.emit('updated'); + cleanup(); + }); + + newExport.once('finished', () => { + newExport.onProgress('Export completed', 1); + newExport.status = 'completed'; + this.status = ConversionStatus.completed; + this.emit('updated'); + cleanup(); + }); + + newExport.once('error', (error: Error) => { + showError(error, {plugin: exportOptions.plugin} as any); + newExport.onProgress('Export failed', 1); + newExport.status = 'failed'; + this.error = error; + this.status = ConversionStatus.failed; + this.emit('updated'); + cleanup(); + }); + + this.currentExport = newExport; + newExport.start(); + } + + cancel = () => { + // Avoid infinite loop + if (!this.conversionProcess?.isCanceled) { + this.conversionProcess?.cancel(); + } + + this.currentExport?.onCancel(); + }; + + private readonly onConversionProgress = (action: string, progress: number, estimate?: string) => { + const text = estimate ? `${action} — ${estimate} remaining` : `${action}…`; + this.onProgress(text, progress); + }; + + private readonly onExportProgress = (text: string, progress: number) => { + this.onProgress(text, progress); + }; + + private readonly calculateFileSize = async (filePath?: string) => { + if (!filePath) { + return; + } + + try { + const {size} = await fs.promises.stat(filePath); + this.finalSize = prettyBytes(size); + this.emit('updated'); + } catch {} + }; + + private readonly start = () => { + this.conversionProcess = convertTo( + this.format, + { + ...this.options, + defaultFileName: this.video.title, + inputPath: this.video.filePath, + onProgress: this.onConversionProgress, + onCancel: this.cancel + }, + this.video.encoding + ); + }; +} + +export const setUpConversionListeners = () => { + ipc.answerRenderer('create-conversion', async ({ + filePath, options, format, plugins: pluginOptions + }: CreateConversionOptions, window) => { + const video = Video.fromId(filePath); + const extras: Record = { + appUrl: pluginOptions.share.app?.url + }; + + if (!video) { + return; + } + + if (pluginOptions.share.pluginName === '_saveToDisk') { + const targetFilePath = await askForTargetFilePath( + window, + format, + video.title + ); + + if (targetFilePath) { + extras.targetFilePath = targetFilePath; + } else { + return; + } + } + + const exportPlugin = plugins.sharePlugins.find(plugin => { + return plugin.name === pluginOptions.share.pluginName; + }); + + const exportService = exportPlugin?.shareServices.find(service => { + return service.title === pluginOptions.share.serviceTitle; + }); + + if (!exportPlugin || !exportService) { + return; + } + + const conversion = Conversion.getOrCreate(video, format, options); + + if (extras.targetFilePath) { + conversion.title = path.parse(extras.targetFilePath).name; + } + + conversion.addExport({ + plugin: exportPlugin, + service: exportService, + extras + }); + + return conversion.id; + }); + + app.on('before-quit', event => { + if (Conversion.all.some(conversion => conversion.status === ConversionStatus.inProgress)) { + windowManager.exports?.open(); + + ensureDockIsShowingSync(() => { + const buttonIndex = dialog.showMessageBoxSync({ + type: 'question', + buttons: [ + 'Continue', + 'Quit' + ], + defaultId: 0, + cancelId: 1, + message: 'Do you want to continue exporting?', + detail: 'Kap is currently exporting files. If you quit, the export task will be canceled.' + }); + + if (buttonIndex === 0) { + event.preventDefault(); + } + }); + } + }); +}; diff --git a/main/convert.js b/main/convert.js deleted file mode 100644 index f33a97cd5..000000000 --- a/main/convert.js +++ /dev/null @@ -1,391 +0,0 @@ -/* eslint-disable array-element-newline */ -'use strict'; - -const os = require('os'); -const path = require('path'); -const execa = require('execa'); -const moment = require('moment'); -const prettyMs = require('pretty-ms'); -const tmp = require('tmp'); -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const util = require('electron-util'); -const PCancelable = require('p-cancelable'); -const tempy = require('tempy'); -const gifsicle = require('gifsicle'); -const {track} = require('./common/analytics'); -const {EditServiceContext} = require('./service-context'); -const settings = require('./common/settings'); - -const gifsiclePath = util.fixPathForAsarUnpack(gifsicle); -const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); -const timeRegex = /time=\s*(\d\d:\d\d:\d\d.\d\d)/gm; -const speedRegex = /speed=\s*(-?\d+(,\d+)*(\.\d+(e\d+)?)?)/gm; - -// https://trac.ffmpeg.org/ticket/309 -const makeEven = n => 2 * Math.round(n / 2); - -const areDimensionsEven = ({width, height}) => width % 2 === 0 && height % 2 === 0; - -const getRunFunction = (shouldTrack, mode = 'convert') => (outputPath, options, args) => { - const modes = new Map([ - ['convert', ffmpegPath], - ['compress', gifsiclePath] - ]); - const program = modes.get(mode); - - return new PCancelable((resolve, reject, onCancel) => { - const runner = execa(program, args); - const durationMs = moment.duration(options.endTime - options.startTime, 'seconds').asMilliseconds(); - let speed; - - onCancel(() => { - if (shouldTrack) { - track('file/export/convert/canceled'); - } - - runner.kill(); - }); - - let stderr = ''; - runner.stderr.setEncoding('utf8'); - runner.stderr.on('data', data => { - stderr += data; - - data = data.trim(); - - const processingSpeed = speedRegex.exec(data); - - if (processingSpeed) { - speed = Number.parseFloat(processingSpeed[1]); - } - - const timeProccessed = timeRegex.exec(data); - - if (timeProccessed && speed > 0) { - const processedMs = moment.duration(timeProccessed[1]).asMilliseconds(); - const progress = processedMs / durationMs; - - // Wait 2 second in the conversion for the speed to be stable - if (processedMs > 2 * 1000) { - const msRemaining = (durationMs - processedMs) / speed; - - options.onProgress(progress, prettyMs(Math.max(msRemaining, 1000), {compact: true})); - } else { - options.onProgress(progress); - } - } - }); - - runner.on('error', reject); - - runner.on('exit', code => { - if (code === 0) { - if (shouldTrack) { - track(`file/export/${mode}/completed`); - } - - resolve(outputPath); - } else { - if (shouldTrack) { - track(`file/export/${mode}/failed`); - } - - reject(new Error(`${program} exited with code: ${code}\n\n${stderr}`)); - } - }); - - runner.catch(reject); - }); -}; - -const mute = PCancelable.fn(async (inputPath, onCancel) => { - const mutedPath = tmp.tmpNameSync({postfix: path.extname(inputPath)}); - const converter = execa(ffmpegPath, [ - '-i', inputPath, - '-an', - '-vcodec', 'copy', - mutedPath - ]); - - onCancel(() => { - converter.kill(); - }); - - await converter; - - return mutedPath; -}); - -const convert = getRunFunction(true); -const compress = (outputPath, options, args) => { - options.onProgress(0, '', 'Compressing'); - - if (settings.get('lossyCompression')) { - args = [ - '--lossy=50', - ...args - ]; - } - - return getRunFunction(true, 'compress')(outputPath, options, args); -}; - -const convertToMp4 = PCancelable.fn(async (options, onCancel) => { - if (options.isMuted) { - const muteProcess = mute(options.inputPath); - - onCancel(() => { - muteProcess.cancel(); - }); - - options.inputPath = await muteProcess; - } - - return convert(options.outputPath, options, [ - '-i', options.inputPath, - '-r', options.fps, - ...( - options.shouldCrop || !areDimensionsEven(options) ? [ - '-s', `${makeEven(options.width)}x${makeEven(options.height)}`, - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - options.outputPath - ]); -}); - -const convertToWebm = PCancelable.fn(async (options, onCancel) => { - if (options.isMuted) { - const muteProcess = mute(options.inputPath); - - onCancel(() => { - muteProcess.cancel(); - }); - - options.inputPath = await muteProcess; - } - - return convert(options.outputPath, options, [ - '-i', options.inputPath, - // http://wiki.webmproject.org/ffmpeg - // https://trac.ffmpeg.org/wiki/Encode/VP9 - '-threads', Math.max(os.cpus().length - 1, 1), - '-deadline', 'good', // `best` is twice as slow and only slighty better - '-b:v', '1M', // Bitrate (same as the MP4) - '-codec:v', 'vp9', - '-codec:a', 'vorbis', - '-ac', '2', // https://stackoverflow.com/questions/19004762/ffmpeg-covert-from-mp4-to-webm-only-working-on-some-files - '-strict', '-2', // Needed because `vorbis` is experimental - '-r', options.fps, - ...( - options.shouldCrop ? [ - '-s', `${options.width}x${options.height}`, - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - options.outputPath - ]); -}); - -const convertToAv1 = PCancelable.fn(async (options, onCancel) => { - if (options.isMuted) { - const muteProcess = mute(options.inputPath); - - onCancel(() => { - muteProcess.cancel(); - }); - - options.inputPath = await muteProcess; - } - - return convert(options.outputPath, options, [ - '-i', options.inputPath, - '-r', options.fps, - ...( - options.shouldCrop || !areDimensionsEven(options) ? [ - '-s', `${makeEven(options.width)}x${makeEven(options.height)}`, - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - '-c:v', 'libaom-av1', - '-c:a', 'libopus', - '-crf', '34', - '-b:v', '0', - '-strict', 'experimental', - // Enables row-based multi-threading which maximizes CPU usage - // https://trac.ffmpeg.org/wiki/Encode/AV1 - '-cpu-used', '4', - '-row-mt', '1', - '-tiles', '2x2', - options.outputPath - ]); -}); - -// Should be similiar to the Gif generation -const convertToApng = options => { - return convert(options.outputPath, options, [ - '-i', options.inputPath, - '-vf', `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''}`, - // Strange for APNG instead of -loop it uses -plays see: https://stackoverflow.com/questions/43795518/using-ffmpeg-to-create-looping-apng - '-plays', options.loop === true ? '0' : '1', // 0 == forever; 1 == no loop - ...( - options.shouldCrop ? [ - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - options.outputPath - ]); -}; - -// `time ffmpeg -i original.mp4 -vf fps=30,scale=480:-1::flags=lanczos,palettegen palette.png` -// `time ffmpeg -i original.mp4 -i palette.png -filter_complex 'fps=30,scale=-1:-1:flags=lanczos[x]; [x][1:v]paletteuse' palette.gif` -const convertToGif = PCancelable.fn(async (options, onCancel) => { - const palettePath = tmp.tmpNameSync({postfix: '.png'}); - const paletteProcessor = execa(ffmpegPath, [ - ...( - options.shouldCrop ? [ - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - '-i', options.inputPath, - '-vf', `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''},palettegen`, - palettePath - ]); - - onCancel(() => { - paletteProcessor.kill(); - }); - - await paletteProcessor; - - await convert(options.outputPath, options, [ - '-i', options.inputPath, - '-i', palettePath, - '-filter_complex', `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''}[x]; [x][1:v]paletteuse`, - '-loop', options.loop === true ? '0' : '-1', // 0 == forever; -1 == no loop - ...( - options.shouldCrop ? [ - '-ss', options.startTime, - '-to', options.endTime - ] : [] - ), - options.outputPath - ]); - - return compress(options.outputPath, options, [ - '--batch', - options.outputPath - ]); -}); - -const converters = new Map([ - ['gif', convertToGif], - ['mp4', convertToMp4], - ['webm', convertToWebm], - ['apng', convertToApng], - ['av1', convertToAv1] -]); - -const convertTo = (options, format) => { - const outputPath = path.join(tempy.directory(), options.defaultFileName); - const converter = converters.get(format); - - if (!converter) { - throw new Error(`Unsupported file format: ${format}`); - } - - options.onProgress(0); - track(`file/export/format/${format}`); - - if (options.editService) { - return convertUsingPlugin({outputPath, format, converter, ...options}); - } - - return converter({outputPath, ...options}); -}; - -const convertUsingPlugin = PCancelable.fn(async ({editService, converter, ...options}, onCancel) => { - let croppedPath; - - if (options.shouldCrop) { - croppedPath = tmp.tmpNameSync({postfix: path.extname(options.inputPath)}); - - editService.setProgress('Cropping…'); - - const cropProcess = execa(ffmpegPath, [ - '-i', options.inputPath, - '-s', `${makeEven(options.width)}x${makeEven(options.height)}`, - '-ss', options.startTime, - '-to', options.endTime, - croppedPath - ]); - - onCancel(() => { - cropProcess.kill(); - }); - - await cropProcess; - } else { - croppedPath = options.inputPath; - } - - let canceled = false; - const convertFunction = getRunFunction(false); - - const editPath = tmp.tmpNameSync({postfix: path.extname(croppedPath)}); - - console.log('Export options', { - ...options, - inputPath: croppedPath, - outputPath: editPath - }); - - const editProcess = editService.service.action( - new EditServiceContext({ - onCancel: editService.cancel, - config: editService.config, - setProgress: editService.setProgress, - convert: (args, progressText = 'Converting') => convertFunction(undefined, { - endTime: options.endTime, - startTime: options.startTime, - onProgress: (percentage, estimate) => editService.setProgress(estimate ? `${progressText} — ${estimate} remaining` : `${progressText}…`, percentage) - }, args), - exportOptions: { - ...options, - inputPath: croppedPath, - outputPath: editPath - } - }) - ); - - if (editProcess.cancel && typeof editProcess.cancel === 'function') { - onCancel(() => { - canceled = true; - editProcess.cancel(); - }); - } - - await editProcess; - - if (canceled) { - return; - } - - track(`plugins/used/edit/${editService.pluginName}`); - - return converter({ - ...options, - shouldCrop: false, - inputPath: editPath - }); -}); - -module.exports = { - convertTo, - converters -}; diff --git a/main/converters/h264.ts b/main/converters/h264.ts new file mode 100644 index 000000000..565f070e0 --- /dev/null +++ b/main/converters/h264.ts @@ -0,0 +1,256 @@ +import PCancelable from 'p-cancelable'; +import tempy from 'tempy'; +import {compress, convert} from './process'; +import {areDimensionsEven, conditionalArgs, ConvertOptions, makeEven} from './utils'; +import {settings} from '../common/settings'; +import os from 'os'; +import {Format} from '../common/types'; +import fs from 'fs'; + +// `time ffmpeg -i original.mp4 -vf fps=30,scale=480:-1::flags=lanczos,palettegen palette.png` +// `time ffmpeg -i original.mp4 -i palette.png -filter_complex 'fps=30,scale=-1:-1:flags=lanczos[x]; [x][1:v]paletteuse' palette.gif` +const convertToGif = PCancelable.fn(async (options: ConvertOptions, onCancel: PCancelable.OnCancelFunction) => { + const palettePath = tempy.file({extension: 'png'}); + + const paletteProcess = convert(palettePath, {shouldTrack: false}, conditionalArgs( + '-i', options.inputPath, + '-vf', `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''},palettegen`, + { + args: [ + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop + }, + palettePath + )); + + onCancel(() => { + paletteProcess.cancel(); + }); + + await paletteProcess; + + // Sometimes if the clip is too short or fps too low, the palette is not generated + const hasPalette = fs.existsSync(palettePath); + + const shouldLoop = settings.get('loopExports'); + + const conversionProcess = convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Converting', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime + }, conditionalArgs( + '-i', options.inputPath, + { + args: [ + '-i', + palettePath, + '-filter_complex', + `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''}[x]; [x][1:v]paletteuse` + ], + if: hasPalette + }, + { + args: [ + '-vf', + `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''}` + ], + if: !hasPalette + }, + '-loop', shouldLoop ? '0' : '-1', // 0 == forever; -1 == no loop + { + args: [ + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop + }, + options.outputPath + )); + + onCancel(() => { + conversionProcess.cancel(); + }); + + await conversionProcess; + + const compressProcess = compress(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Compressing', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime + }, [ + '--batch', + options.outputPath + ]); + + onCancel(() => { + compressProcess.cancel(); + }); + + await compressProcess; + + return options.outputPath; +}); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +const convertToMp4 = (options: ConvertOptions) => convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Converting', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime +}, conditionalArgs( + '-i', options.inputPath, + '-r', options.fps.toString(), + { + args: ['-an'], + if: options.shouldMute + }, + { + args: [ + '-s', + `${makeEven(options.width)}x${makeEven(options.height)}`, + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop || !areDimensionsEven(options) + }, + options.outputPath +)); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +const convertToWebm = (options: ConvertOptions) => convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Converting', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime +}, conditionalArgs( + '-i', options.inputPath, + // http://wiki.webmproject.org/ffmpeg + // https://trac.ffmpeg.org/wiki/Encode/VP9 + '-threads', Math.max(os.cpus().length - 1, 1).toString(), + '-deadline', 'good', // `best` is twice as slow and only slighty better + '-b:v', '1M', // Bitrate (same as the MP4) + '-codec:v', 'vp9', + '-codec:a', 'vorbis', + '-ac', '2', // https://stackoverflow.com/questions/19004762/ffmpeg-covert-from-mp4-to-webm-only-working-on-some-files + '-strict', '-2', // Needed because `vorbis` is experimental + '-r', options.fps.toString(), + { + args: ['-an'], + if: options.shouldMute + }, + { + args: [ + '-s', + `${makeEven(options.width)}x${makeEven(options.height)}`, + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop || !areDimensionsEven(options) + }, + options.outputPath +)); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +const convertToAv1 = (options: ConvertOptions) => convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Converting', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime +}, conditionalArgs( + '-i', options.inputPath, + '-r', options.fps.toString(), + '-c:v', 'libaom-av1', + '-c:a', 'libopus', + '-crf', '34', + '-b:v', '0', + '-strict', 'experimental', + // Enables row-based multi-threading which maximizes CPU usage + // https://trac.ffmpeg.org/wiki/Encode/AV1 + '-cpu-used', '4', + '-row-mt', '1', + '-tiles', '2x2', + { + args: ['-an'], + if: options.shouldMute + }, + { + args: [ + '-s', + `${makeEven(options.width)}x${makeEven(options.height)}`, + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop || !areDimensionsEven(options) + }, + options.outputPath +)); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +const convertToApng = (options: ConvertOptions) => convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Converting', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime +}, conditionalArgs( + '-i', options.inputPath, + '-vf', `fps=${options.fps}${options.shouldCrop ? `,scale=${options.width}:${options.height}:flags=lanczos` : ''}`, + // Strange for APNG instead of -loop it uses -plays see: https://stackoverflow.com/questions/43795518/using-ffmpeg-to-create-looping-apng + '-plays', settings.get('loopExports') ? '0' : '1', // 0 == forever; 1 == no loop + { + args: ['-an'], + if: options.shouldMute + }, + { + args: [ + '-ss', + options.startTime.toString(), + '-to', + options.endTime.toString() + ], + if: options.shouldCrop + }, + options.outputPath +)); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export const crop = (options: ConvertOptions) => convert(options.outputPath, { + onProgress: (progress, estimate) => { + options.onProgress('Cropping', progress, estimate); + }, + startTime: options.startTime, + endTime: options.endTime +}, conditionalArgs( + '-i', options.inputPath, + '-s', `${makeEven(options.width)}x${makeEven(options.height)}`, + '-ss', options.startTime.toString(), + '-to', options.endTime.toString(), + options.outputPath +)); + +export default new Map([ + [Format.gif, convertToGif], + [Format.mp4, convertToMp4], + [Format.webm, convertToWebm], + [Format.apng, convertToApng], + [Format.av1, convertToAv1] +]); diff --git a/main/converters/index.ts b/main/converters/index.ts new file mode 100644 index 000000000..72bb21ac1 --- /dev/null +++ b/main/converters/index.ts @@ -0,0 +1,172 @@ +import path from 'path'; +import tempy from 'tempy'; +import {Encoding, Format} from '../common/types'; +import {track} from '../common/analytics'; +import h264Converters, {crop as h264Crop} from './h264'; +import {ConvertOptions} from './utils'; +import {getFormatExtension} from '../common/constants'; +import PCancelable, {OnCancelFunction} from 'p-cancelable'; +import {convert} from './process'; +import {plugins} from '../plugins'; +import {EditServiceContext} from '../plugins/service-context'; +import {settings} from '../common/settings'; +import {Except} from 'type-fest'; + +const converters = new Map([ + [Encoding.h264, h264Converters] +]); + +const croppingHandlers = new Map([ + [Encoding.h264, h264Crop] +]); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export const convertTo = ( + format: Format, + options: Except & {defaultFileName: string}, + encoding: Encoding = Encoding.h264 +) => { + if (!converters.has(encoding)) { + throw new Error(`Unsupported encoding: ${encoding}`); + } + + const converter = converters.get(encoding)?.get(format); + + if (!converter) { + throw new Error(`Unsupported file format for ${encoding}: ${format}`); + } + + track(`file/export/encoding/${encoding}`); + track(`file/export/format/${format}`); + + const conversionOptions = { + outputPath: path.join(tempy.directory(), `${options.defaultFileName}.${getFormatExtension(format)}`), + ...options + }; + + if (options.editService) { + const croppingHandler = croppingHandlers.get(encoding); + + if (!croppingHandler) { + throw new Error(`Unsupported encoding: ${encoding}`); + } + + return convertWithEditPlugin({...conversionOptions, format, croppingHandler, converter}); + } + + return converter(conversionOptions); +}; + +const convertWithEditPlugin = PCancelable.fn( + async ( + options: ConvertOptions & { + format: Format; + converter: (options: ConvertOptions) => PCancelable; + croppingHandler: (options: ConvertOptions) => PCancelable; + }, + onCancel: OnCancelFunction + ) => { + let croppedPath: string; + + if (options.shouldCrop) { + croppedPath = tempy.file({extension: path.extname(options.inputPath)}); + + options.onProgress('Cropping', 0); + + const cropProcess = options.croppingHandler({ + ...options, + outputPath: croppedPath + }); + + onCancel(() => { + cropProcess.cancel(); + }); + + await cropProcess; + } else { + croppedPath = options.inputPath; + } + + let isCanceled = false; + + // eslint-disable-next-line @typescript-eslint/promise-function-async + const convertFunction = (args: string[], text = 'Converting') => new PCancelable(async (resolve, reject, onCancel) => { + try { + const process = convert( + '', { + shouldTrack: false, + startTime: options.startTime, + endTime: options.endTime, + onProgress: (progress, estimate) => { + options.onProgress(text, progress, estimate); + } + }, args + ); + + onCancel(() => { + process.cancel(); + }); + await process; + resolve(); + } catch (error) { + reject(error); + } + }); + + const editPath = tempy.file({extension: path.extname(croppedPath)}); + + const editPlugin = plugins.editPlugins.find(plugin => { + return plugin.name === options.editService?.pluginName; + }); + + const editService = editPlugin?.editServices.find(service => { + return service.title === options.editService?.serviceTitle; + }); + + if (!editService || !editPlugin) { + throw new Error(`Edit service ${options.editService?.serviceTitle} not found`); + } + + const editProcess = editService.action( + new EditServiceContext({ + plugin: editPlugin, + onCancel: options.onCancel, + onProgress: options.onProgress, + convert: convertFunction, + inputPath: croppedPath, + outputPath: editPath, + exportOptions: { + width: options.width, + height: options.height, + format: options.format, + fps: options.fps, + duration: options.endTime - options.startTime, + isMuted: options.shouldMute, + loop: settings.get('loopExports') + } + }) + ); + + onCancel(() => { + isCanceled = true; + // @ts-expect-error + if (editProcess.cancel && typeof editProcess.cancel === 'function') { + (editProcess as PCancelable).cancel(); + } + }); + + await editProcess; + + if (isCanceled) { + return ''; + } + + track(`plugins/used/edit/${options.editService?.pluginName}`); + + return options.converter({ + ...options, + shouldCrop: false, + inputPath: editPath + }); + } +); diff --git a/main/converters/process.ts b/main/converters/process.ts new file mode 100644 index 000000000..20b1e1e3d --- /dev/null +++ b/main/converters/process.ts @@ -0,0 +1,136 @@ +import util from 'electron-util'; +import execa from 'execa'; +import moment from 'moment'; +import PCancelable from 'p-cancelable'; +import tempy from 'tempy'; +import path from 'path'; + +import {track} from '../common/analytics'; +import {conditionalArgs, extractProgressFromStderr} from './utils'; +import {settings} from '../common/settings'; + +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); +const gifsicle = require('gifsicle'); + +const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); +const gifsiclePath = util.fixPathForAsarUnpack(gifsicle); + +enum Mode { + convert, + compress +} + +const modes = new Map([ + [Mode.convert, ffmpegPath], + [Mode.compress, gifsiclePath] +]); + +export interface ProcessOptions { + shouldTrack?: boolean; + startTime?: number; + endTime?: number; + onProgress?: (progress: number, estimate?: string) => void; +} + +const defaultProcessOptions = { + shouldTrack: true +}; + +const createProcess = (mode: Mode) => { + const program = modes.get(mode)!; + + // eslint-disable-next-line @typescript-eslint/promise-function-async + return (outputPath: string, options: ProcessOptions, args: string[]) => { + const { + shouldTrack, + startTime = 0, + endTime = 0, + onProgress + } = { + ...defaultProcessOptions, + ...options + }; + + const modeName = Mode[mode]; + const trackConversionEvent = (eventName: string) => { + if (shouldTrack) { + track(`file/export/${modeName}/${eventName}`); + } + }; + + return new PCancelable((resolve, reject, onCancel) => { + const runner = execa(program, args); + const conversionStartTime = Date.now(); + + onCancel(() => { + trackConversionEvent('canceled'); + runner.kill(); + }); + + const durationMs = moment.duration(endTime - startTime, 'seconds').asMilliseconds(); + + let stderr = ''; + runner.stderr?.setEncoding('utf8'); + runner.stderr?.on('data', data => { + stderr += data; + + const progressData = extractProgressFromStderr(data, conversionStartTime, durationMs); + + if (progressData) { + onProgress?.(progressData.progress, progressData.estimate); + } + }); + + const failWithError = (reason: unknown) => { + trackConversionEvent('failed'); + reject(reason); + }; + + runner.on('error', failWithError); + + runner.on('exit', code => { + if (code === 0) { + trackConversionEvent('completed'); + resolve(outputPath); + } else { + failWithError(new Error(`${program} exited with code: ${code ?? 0}\n\n${stderr}`)); + } + }); + + runner.catch(failWithError); + }); + }; +}; + +export const convert = createProcess(Mode.convert); +const compressFunction = createProcess(Mode.compress); + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export const compress = (outputPath: string, options: ProcessOptions, args: string[]) => { + const useLossy = settings.get('lossyCompression', false); + + return compressFunction( + outputPath, + options, + conditionalArgs(args, {args: ['--lossy=50'], if: useLossy}) + ); +}; + +export const mute = PCancelable.fn(async (inputPath: string, onCancel: PCancelable.OnCancelFunction) => { + const mutedPath = tempy.file({extension: path.extname(inputPath)}); + + const converter = convert(mutedPath, {shouldTrack: false}, [ + '-i', + inputPath, + '-an', + '-vcodec', + 'copy', + mutedPath + ]); + + onCancel(() => { + converter.cancel(); + }); + + return converter; +}); diff --git a/main/converters/utils.ts b/main/converters/utils.ts new file mode 100644 index 000000000..b16814d81 --- /dev/null +++ b/main/converters/utils.ts @@ -0,0 +1,71 @@ +import moment from 'moment'; +import prettyMilliseconds from 'pretty-ms'; + +export interface ConvertOptions { + inputPath: string; + outputPath: string; + shouldCrop: boolean; + startTime: number; + endTime: number; + width: number; + height: number; + fps: number; + shouldMute: boolean; + onCancel: () => void; + onProgress: (action: string, progress: number, estimate?: string) => void; + editService?: { + pluginName: string; + serviceTitle: string; + }; +} + +export const makeEven = (number: number) => 2 * Math.round(number / 2); + +export const areDimensionsEven = ({width, height}: {width: number; height: number}) => width % 2 === 0 && height % 2 === 0; + +export const extractProgressFromStderr = (stderr: string, conversionStartTime: number, durationMs: number) => { + const conversionDuration = Date.now() - conversionStartTime; + const data = stderr.trim(); + + const speed = Number.parseFloat(/speed=\s*(-?\d+(,\d+)*(\.\d+(e\d+)?)?)/gm.exec(data)?.[1] ?? '0'); + const processedMs = moment.duration(/time=\s*(\d\d:\d\d:\d\d.\d\d)/gm.exec(data)?.[1] ?? 0).asMilliseconds(); + + if (speed > 0) { + const progress = processedMs / durationMs; + + // Wait 2 seconds in the conversion for speed to be stable + // Either 2 seconds of the video or 15 seconds real time (for super slow conversion like AV1) + if (processedMs > 2 * 1000 || conversionDuration > 15 * 1000) { + const msRemaining = (durationMs - processedMs) / speed; + + return { + progress, + estimate: prettyMilliseconds(Math.max(msRemaining, 1000), {compact: true}) + }; + } + + return {progress}; + } + + return undefined; +}; + +type ArgType = string[] | string | { args: string[]; if: boolean }; + +// Resolve conditional args +// +// conditionalArgs(['default', 'args'], {args: ['ignore', 'these'], if: false}); +// => ['default', 'args'] +export const conditionalArgs = (...args: ArgType[]): string[] => { + return args.flatMap(arg => { + if (typeof arg === 'string') { + return [arg]; + } + + if (Array.isArray(arg)) { + return arg; + } + + return arg.if ? arg.args : []; + }); +}; diff --git a/main/editor.js b/main/editor.js deleted file mode 100644 index 719aadeba..000000000 --- a/main/editor.js +++ /dev/null @@ -1,150 +0,0 @@ -'use strict'; - -const {BrowserWindow, dialog} = require('electron'); -const path = require('path'); -const fs = require('fs'); -const EventEmitter = require('events'); -const pify = require('pify'); -const {ipcMain: ipc} = require('electron-better-ipc'); -const {is} = require('electron-util'); - -const getFps = require('./utils/fps'); -const loadRoute = require('./utils/routes'); -const {generateTimestampedName} = require('./utils/timestamped-name'); - -const editors = new Map(); -let allOptions; -const OPTIONS_BAR_HEIGHT = 48; -const VIDEO_ASPECT = 9 / 16; -const MIN_VIDEO_WIDTH = 768; -const MIN_VIDEO_HEIGHT = MIN_VIDEO_WIDTH * VIDEO_ASPECT; -const MIN_WINDOW_HEIGHT = MIN_VIDEO_HEIGHT + OPTIONS_BAR_HEIGHT; -const editorEmitter = new EventEmitter(); -const editorsWithNotSavedDialogs = new Map(); - -const getEditorName = (filePath, isNewRecording) => isNewRecording ? generateTimestampedName() : path.basename(filePath); - -const openEditorWindow = async ( - filePath, - { - recordedFps, - isNewRecording, - originalFilePath, - recordingName - } = {} -) => { - if (editors.has(filePath)) { - editors.get(filePath).show(); - return; - } - - const fps = recordedFps || await getFps(filePath); - const title = recordingName || getEditorName(originalFilePath || filePath, isNewRecording); - - const editorWindow = new BrowserWindow({ - title, - minWidth: MIN_VIDEO_WIDTH, - minHeight: MIN_WINDOW_HEIGHT, - width: MIN_VIDEO_WIDTH, - height: MIN_WINDOW_HEIGHT, - webPreferences: { - nodeIntegration: true, - webSecurity: !is.development // Disable webSecurity in dev to load video over file:// protocol while serving over insecure http, this is not needed in production where we use file:// protocol for html serving. - }, - frame: false, - transparent: true, - vibrancy: 'window', - show: false - }); - - editors.set(filePath, editorWindow); - - loadRoute(editorWindow, 'editor'); - - if (isNewRecording) { - editorWindow.setDocumentEdited(true); - editorWindow.on('close', event => { - editorsWithNotSavedDialogs.set(filePath, true); - const buttonIndex = dialog.showMessageBoxSync(editorWindow, { - type: 'question', - buttons: [ - 'Discard', - 'Cancel' - ], - defaultId: 0, - cancelId: 1, - message: 'Are you sure that you want to discard this recording?', - detail: 'You will no longer be able to edit and export the original recording.' - }); - - if (buttonIndex === 1) { - event.preventDefault(); - } - - editorsWithNotSavedDialogs.delete(filePath); - }); - } - - editorWindow.on('closed', () => { - editors.delete(filePath); - }); - - editorWindow.on('blur', () => { - editorEmitter.emit('blur'); - ipc.callRenderer(editorWindow, 'blur'); - }); - editorWindow.on('focus', () => { - editorEmitter.emit('focus'); - ipc.callRenderer(editorWindow, 'focus'); - }); - - editorWindow.webContents.on('did-finish-load', async () => { - ipc.callRenderer(editorWindow, 'export-options', allOptions); - await ipc.callRenderer(editorWindow, 'file', { - filePath, - fps, - originalFilePath, - isNewRecording, - recordingName, - title - }); - editorWindow.show(); - }); -}; - -const setOptions = options => { - allOptions = options; -}; - -const getEditors = () => editors.values(); - -const getEditor = path => editors.get(path); - -ipc.answerRenderer('save-original', async ({inputPath}) => { - const {filePath} = await dialog.showSaveDialog(BrowserWindow.getFocusedWindow(), { - defaultPath: generateTimestampedName('Kapture', '.mp4') - }); - - if (filePath) { - await pify(fs.copyFile)(inputPath, filePath, fs.constants.COPYFILE_FICLONE); - } -}); - -const checkForAnyBlockingEditors = () => { - if (editorsWithNotSavedDialogs.size > 0) { - const [path] = editorsWithNotSavedDialogs.keys(); - editors.get(path).focus(); - return true; - } - - return false; -}; - -module.exports = { - openEditorWindow, - setOptions, - getEditors, - getEditor, - editorEmitter, - checkForAnyBlockingEditors -}; diff --git a/main/export-list.js b/main/export-list.js deleted file mode 100644 index d4e46e69f..000000000 --- a/main/export-list.js +++ /dev/null @@ -1,295 +0,0 @@ -/* eslint-disable array-element-newline */ -'use strict'; -const {dialog, BrowserWindow, app, Notification} = require('electron'); -const fs = require('fs'); -const {dirname} = require('path'); -const {ipcMain: ipc} = require('electron-better-ipc'); -const base64Img = require('base64-img'); -const tmp = require('tmp'); -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const util = require('electron-util'); -const execa = require('execa'); -const makeDir = require('make-dir'); - -const settings = require('./common/settings'); -const {track} = require('./common/analytics'); -const {openPrefsWindow} = require('./preferences'); -const {getExportsWindow, openExportsWindow} = require('./exports'); -const {openEditorWindow} = require('./editor'); -const {toggleExportMenuItem} = require('./menus'); -const Export = require('./export'); -const {ensureDockIsShowingSync} = require('./utils/dock'); -const {generateTimestampedName} = require('./utils/timestamped-name'); - -const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); -let lastSavedDirectory; - -const filterMap = new Map([ - ['mp4', [{name: 'Movies', extensions: ['mp4']}]], - ['webm', [{name: 'Movies', extensions: ['webm']}]], - ['gif', [{name: 'Images', extensions: ['gif']}]], - ['apng', [{name: 'Images', extensions: ['apng']}]] -]); - -const getPreview = async inputPath => { - const previewPath = tmp.tmpNameSync({postfix: '.jpg'}); - await execa(ffmpegPath, [ - '-ss', 0, - '-i', inputPath, - '-t', 1, - '-vframes', 1, - '-f', 'image2', - previewPath - ]); - - return previewPath; -}; - -const getDragIcon = async inputPath => { - const iconPath = tmp.tmpNameSync({postfix: '.jpg'}); - await execa(ffmpegPath, [ - '-i', inputPath, - // Scale the largest dimension to 64px maintaining aspect ratio - '-vf', 'scale=if(gte(iw\\,ih)\\,min(64\\,iw)\\,-2):if(lt(iw\\,ih)\\,min(64\\,ih)\\,-2)', - iconPath - ]); - - return iconPath; -}; - -const saveSnapshot = async ({inputPath, time}) => { - const {filePath: outputPath} = await dialog.showSaveDialog(BrowserWindow.getFocusedWindow(), { - defaultPath: generateTimestampedName('Snapshot', '.jpg') - }); - - if (outputPath) { - await execa(ffmpegPath, [ - '-i', inputPath, - '-ss', time, - '-vframes', 1, - outputPath - ]); - } -}; - -class ExportList { - constructor() { - this.exports = []; - this.queue = []; - } - - _startNext() { - if (this.queue.length === 0) { - return; - } - - this.currentExport = this.queue.shift(); - if (this.currentExport.canceled) { - delete this.currentExport; - this._startNext(); - return; - } - - (async () => { - try { - await this.currentExport.run(); - delete this.currentExport; - this._startNext(); - } catch (error) { - console.log(error); - this.currentExport.updateExport({ - status: 'failed', - text: 'Export failed', - error: {stack: error.stack, message: error.message} - }); - - const notification = new Notification({ - title: this.currentExport.pluginName, - body: error.message - }); - notification.show(); - - delete this.currentExport; - this._startNext(); - } - })(); - } - - cancelExport(createdAt) { - if (this.currentExport && this.currentExport.createdAt === createdAt) { - track('export/canceled/current'); - this.currentExport.cancel(); - } else { - const exportToCancel = this.exports.find(exp => exp.createdAt === createdAt); - if (exportToCancel) { - track('export/canceled/waiting'); - exportToCancel.cancel(); - } - } - } - - async addExport(options) { - options.exportOptions.loop = settings.get('loopExports'); - const newExport = new Export(options); - const createdAt = (new Date()).toISOString(); - - if (options.sharePlugin.pluginName === '_saveToDisk') { - const wasExportsWindowOpen = Boolean(getExportsWindow()); - const exportsWindow = await openExportsWindow(); - const kapturesDir = settings.get('kapturesDir'); - await makeDir(kapturesDir); - - const filters = filterMap.get(options.format); - - const {filePath} = await dialog.showSaveDialog(exportsWindow, { - title: newExport.defaultFileName, - defaultPath: `${lastSavedDirectory || kapturesDir}/${newExport.defaultFileName}`, - filters - }); - - if (filePath) { - newExport.context.targetFilePath = filePath; - lastSavedDirectory = dirname(filePath); - } else { - if (!wasExportsWindowOpen) { - exportsWindow.close(); - } - - return; - } - } else if (options.sharePlugin.pluginName === '_openWith') { - newExport.context.appUrl = options.openWithApp.url; - } - - if (!newExport.shareConfig.isConfigValid()) { - const result = dialog.showMessageBoxSync({ - type: 'error', - buttons: ['Configure', 'Cancel'], - defaultId: 0, - message: 'Error in plugin config', - detail: `Review the config for the "${options.sharePlugin.pluginName}" plugin to continue exporting`, - cancelId: 1 - }); - - track(`export/plugin/invalid/${options.sharePlugin.pluginName}`); - - if (result === 0) { - const prefsWindow = await openPrefsWindow(); - ipc.callRenderer(prefsWindow, 'open-plugin-config', options.sharePlugin.pluginName); - } - - return; - } - - toggleExportMenuItem(true); - - newExport.status = 'waiting'; - newExport.text = 'Waiting…'; - newExport.imagePath = await getPreview(options.inputPath); - newExport.image = base64Img.base64Sync(newExport.imagePath); - newExport.createdAt = createdAt; - newExport.originalFps = options.originalFps; - - callExportsWindow('update-export', {...newExport.data, createdAt}); - openExportsWindow(); - - newExport.updateExport = updates => { - if (newExport.canceled) { - return; - } - - for (const key in updates) { - if (updates[key] !== undefined) { - newExport[key] = updates[key]; - } - } - - callExportsWindow('update-export', {...newExport.data, createdAt}); - }; - - this.exports.push(newExport); - this.queue.push(newExport); - track(`export/queued/${this.queue.length}`); - - if (!this.currentExport) { - this._startNext(); - } - } - - getExports() { - return this.exports.map(exp => exp.data); - } - - getExport(createdAt) { - return this.exports.find(exp => exp.createdAt === createdAt); - } - - openExport(createdAt) { - track('export/history/opened/recording'); - const exp = this.getExport(createdAt); - if (exp) { - openEditorWindow(exp.previewPath, {recordedFps: exp.originalFps, originalFilePath: exp.inputPath}); - } - } -} - -let exportList; - -ipc.answerRenderer('get-exports', () => exportList.getExports()); - -ipc.answerRenderer('export', options => exportList.addExport(options)); - -ipc.answerRenderer('cancel-export', createdAt => exportList.cancelExport(createdAt)); - -ipc.answerRenderer('open-export', createdAt => exportList.openExport(createdAt)); - -ipc.answerRenderer('export-snapshot', saveSnapshot); - -ipc.on('drag-export', async (event, createdAt) => { - const exportItem = exportList.getExport(createdAt); - const file = exportItem && exportItem.data.filePath; - - if (file && fs.existsSync(file)) { - event.sender.startDrag({ - file, - icon: await getDragIcon(exportItem.imagePath) - }); - } -}); - -const callExportsWindow = (channel, data) => { - const exportsWindow = getExportsWindow(); - - if (exportsWindow) { - // TODO(karaggeorge): Investigate why `ipc.callRenderer(exportsWindow, channel, data);` is not working here. - exportsWindow.webContents.send(channel, data); - } -}; - -module.exports = () => { - exportList = new ExportList(); - - app.on('before-quit', event => { - if (exportList.currentExport) { - openExportsWindow(); - - ensureDockIsShowingSync(() => { - const buttonIndex = dialog.showMessageBoxSync({ - type: 'question', - buttons: [ - 'Continue', - 'Quit' - ], - defaultId: 0, - cancelId: 1, - message: 'Do you want to continue exporting?', - detail: 'Kap is currently exporting files. If you quit, the export task will be canceled.' - }); - - if (buttonIndex === 0) { - event.preventDefault(); - } - }); - } - }); -}; diff --git a/main/export-options.js b/main/export-options.js deleted file mode 100644 index 2e0447c31..000000000 --- a/main/export-options.js +++ /dev/null @@ -1,171 +0,0 @@ -'use strict'; - -const Store = require('electron-store'); -const {ipcMain: ipc} = require('electron-better-ipc'); - -const plugins = require('./common/plugins'); -const {converters} = require('./convert'); -const {setOptions, getEditors} = require('./editor'); -const {apps} = require('./plugins/open-with-plugin'); -const {showError} = require('./utils/errors'); - -const exportUsageHistory = new Store({ - name: 'export-usage-history', - defaults: { - apng: {lastUsed: 1, plugins: {default: 1}}, - webm: {lastUsed: 2, plugins: {default: 1}}, - mp4: {lastUsed: 3, plugins: {default: 1}}, - gif: {lastUsed: 4, plugins: {default: 1}}, - av1: {lastUsed: 5, plugins: {default: 1}} - } -}); - -const fpsUsageHistory = new Store({ - name: 'fps-usage-history', - schema: { - apng: { - type: 'number', - minimum: 0, - default: 60 - }, - webm: { - type: 'number', - minimum: 0, - default: 60 - }, - mp4: { - type: 'number', - minimum: 0, - default: 60 - }, - gif: { - type: 'number', - minimum: 0, - default: 60 - }, - av1: { - type: 'number', - minimum: 0, - default: 60 - } - } -}); - -const prettifyFormat = format => { - const formats = new Map([ - ['apng', 'APNG'], - ['gif', 'GIF'], - ['mp4', 'MP4 (H264)'], - ['av1', 'MP4 (AV1)'], - ['webm', 'WebM'] - ]); - - return formats.get(format); -}; - -const getEditOptions = () => { - const installed = plugins.getEditPlugins(); - - return installed.flatMap( - plugin => plugin.editServices - .filter(service => plugin.config.validServices.includes(service.title)) - .map(service => ({ - title: service.title, - pluginName: plugin.name, - pluginPath: plugin.pluginPath, - hasConfig: Object.keys(service.config || {}).length > 0 - })) - ); -}; - -const getExportOptions = () => { - const installed = plugins.getSharePlugins(); - const builtIn = plugins.getBuiltIn(); - - const options = []; - for (const format of converters.keys()) { - options.push({ - format, - prettyFormat: prettifyFormat(format), - plugins: [] - }); - } - - for (const json of [...installed, ...builtIn]) { - if (!json.isCompatible) { - continue; - } - - try { - const plugin = require(json.pluginPath); - - for (const service of plugin.shareServices) { - for (const format of service.formats) { - options.find(option => option.format === format).plugins.push({ - title: service.title, - pluginName: json.name, - pluginPath: json.pluginPath, - apps: json.name === '_openWith' ? apps.get(format) : undefined - }); - } - } - } catch (error) { - showError(error, {title: `Something went wrong while loading “${json.name}”`, plugin: json}); - const Sentry = require('./utils/sentry'); - Sentry.captureException(error); - } - } - - const sortFunc = (a, b) => b.lastUsed - a.lastUsed; - - for (const option of options) { - const {lastUsed, plugins} = exportUsageHistory.get(option.format); - option.lastUsed = lastUsed; - option.plugins = option.plugins.map(plugin => ({...plugin, lastUsed: plugins[plugin.pluginName] || 0})).sort(sortFunc); - } - - return options.sort(sortFunc); -}; - -const updateExportOptions = () => { - const editors = getEditors(); - const exportOptions = getExportOptions(); - const editOptions = getEditOptions(); - for (const editor of editors) { - ipc.callRenderer(editor, 'export-options', {exportOptions, editOptions, fps: fpsUsageHistory.store}); - } - - setOptions({exportOptions, editOptions, fps: fpsUsageHistory.store}); -}; - -plugins.setUpdateExportOptions(updateExportOptions); - -ipc.answerRenderer('update-usage', ({format, plugin, fps}) => { - if (plugin) { - const usage = exportUsageHistory.get(format); - const now = Date.now(); - - usage.plugins[plugin] = now; - usage.lastUsed = now; - exportUsageHistory.set(format, usage); - } - - fpsUsageHistory.set(format, fps); - updateExportOptions(); -}); - -ipc.answerRenderer('refresh-usage', updateExportOptions); - -const initializeExportOptions = () => { - setOptions({ - exportOptions: getExportOptions(), - editOptions: getEditOptions(), - fps: fpsUsageHistory.store - }); -}; - -module.exports = { - getExportOptions, - updateExportOptions, - initializeExportOptions -}; diff --git a/main/export.js b/main/export.js deleted file mode 100644 index 510ccf7ce..000000000 --- a/main/export.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; - -const path = require('path'); -const PCancelable = require('p-cancelable'); - -const {track} = require('./common/analytics'); -const {convertTo} = require('./convert'); -const {ShareServiceContext} = require('./service-context'); -const {getFormatExtension} = require('./common/constants'); -const PluginConfig = require('./utils/plugin-config'); -const {generateTimestampedName} = require('./utils/timestamped-name'); - -class Export { - constructor(options) { - this.exportOptions = options.exportOptions; - this.inputPath = options.inputPath; - this.previewPath = options.previewPath; - - this.sharePluginName = options.sharePlugin.pluginName; - this.sharePlugin = require(options.sharePlugin.pluginPath); - this.shareService = this.sharePlugin.shareServices.find(shareService => shareService.title === options.sharePlugin.serviceTitle); - - this.shareConfig = new PluginConfig({ - allServices: [this.shareService], - name: this.sharePluginName - }); - - if (options.editPlugin) { - this.editPluginName = options.editPlugin.pluginName; - this.editPlugin = require(options.editPlugin.pluginPath); - this.editService = this.editPlugin.editServices.find(editService => editService.title === options.editPlugin.title); - - this.editConfig = new PluginConfig({ - allServices: [this.editService], - name: this.editPluginName - }); - } - - this.format = options.format; - this.image = ''; - this.isSaveFileService = options.sharePlugin.pluginName === '_saveToDisk'; - this.disableOutputActions = false; - - const fileName = options.recordingName || (options.isNewRecording ? generateTimestampedName('Kapture') : path.parse(this.inputPath).name); - this.defaultFileName = `${fileName}.${getFormatExtension(this.format)}`; - - this.context = new ShareServiceContext({ - _isBuiltin: options.sharePlugin.pluginName.startsWith('_'), - format: this.format, - defaultFileName: this.defaultFileName, - config: this.shareConfig, - onCancel: this.cancel.bind(this), - onProgress: this.setProgress.bind(this), - convert: this.convert.bind(this), - pluginName: this.sharePluginName - }); - - this.run = this.run.bind(this); - - this.setProgress = this.setProgress.bind(this); - this.cancel = this.cancel.bind(this); - } - - get data() { - return { - defaultFileName: this.isSaveFileService ? path.basename(this.context.targetFilePath) : this.defaultFileName, - text: this.text, - status: this.status, - percentage: this.percentage || 0, - image: this.image, - createdAt: this.createdAt, - filePath: this.filePath && (this.isSaveFileService ? this.context.targetFilePath : this.filePath), - error: this.error, - disableOutputActions: this.disableOutputActions - }; - } - - run() { - track(`export/started/${this.sharePluginName}`); - track(`plugins/used/share/${this.sharePluginName}`); - return new PCancelable(async (resolve, reject, onCancel) => { - this.resolve = resolve; - this.reject = reject; - - onCancel(() => this.context.clear()); - try { - await this.shareService.action(this.context); - if (!this.canceled) { - this.updateExport({ - text: 'Export completed', - status: 'completed', - percentage: undefined - }); - } - - resolve(); - } catch (error) { - reject(error); - } - }); - } - - cancel() { - this.updateExport({ - text: 'Export canceled', - status: 'canceled', - percentage: undefined - }); - this.canceled = true; - - if (this.resolve) { - this.context.clear(); - - if (this.convertProcess) { - this.convertProcess.cancel(); - } - - this.resolve(); - } - } - - setProgress(text, percentage = 0) { - this.updateExport({ - text, percentage, - status: 'processing' - }); - } - - async convert({fileType} = {}) { - if (fileType) { - this.disableOutputActions = true; - } - - this.convertProcess = convertTo( - { - ...this.exportOptions, - defaultFileName: fileType ? `${path.parse(this.defaultFileName).name}.${getFormatExtension(fileType)}` : this.defaultFileName, - inputPath: this.inputPath, - onProgress: (percentage, estimate, action = 'Converting') => this.setProgress(estimate ? `${action} — ${estimate} remaining` : `${action}…`, percentage), - editService: this.editService ? { - service: this.editService, - config: this.editConfig, - cancel: this.cancel, - setProgress: this.setProgress, - pluginName: this.editPluginName - } : undefined - }, - fileType || this.format - ); - - this.filePath = await this.convertProcess; - this.resolve(); - return this.filePath; - } -} - -module.exports = Export; diff --git a/main/export.ts b/main/export.ts new file mode 100644 index 000000000..3db2e6460 --- /dev/null +++ b/main/export.ts @@ -0,0 +1,158 @@ +import {ipcMain} from 'electron'; +import {EventEmitter} from 'events'; +import PCancelable, {OnCancelFunction} from 'p-cancelable'; +import Conversion from './conversion'; +import {InstalledPlugin} from './plugins/plugin'; +import {ShareService} from './plugins/service'; +import {ShareServiceContext} from './plugins/service-context'; +import {prettifyFormat} from './utils/formats'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import {setExportMenuItemState} from './menus/utils'; + +export interface ExportOptions { + plugin: InstalledPlugin; + service: ShareService; + extras: Record; +} + +export default class Export extends EventEmitter { + static exportsMap = new Map(); + + static get all() { + return [...this.exportsMap.values()]; + } + + conversion: Conversion; + options: ExportOptions; + context: ShareServiceContext; + createdAt: number = Date.now(); + status = 'waiting'; + text = ''; + percentage = 0; + + private process?: PCancelable; + + private disableOutputActions = false; + + private readonly _start = PCancelable.fn(async (onCancel: OnCancelFunction) => { + const action = this.options.service.action(this.context) as any; + + onCancel(() => { + if (action.cancel && typeof action.cancel === 'function') { + action.cancel(); + } + + this.context.isCanceled = true; + }); + + try { + await action; + this.emit('finished'); + } catch (error) { + if (!error.isCanceled) { + this.emit('error', error); + } + } + }); + + constructor(conversion: Conversion, options: ExportOptions) { + super(); + this.conversion = conversion; + this.options = options; + + this.conversion.video.generatePreviewImage(); + + this.context = new ShareServiceContext({ + plugin: options.plugin, + format: conversion.format, + prettyFormat: prettifyFormat(conversion.format), + defaultFileName: conversion.video.title, + filePath: conversion.filePath, + onProgress: this.onProgress, + onCancel: this.onCancel + }); + + // Used for built-in plugins like save-to-disk + for (const [key, value] of Object.entries(options.extras)) { + (this.context as any)[key] = value; + } + + Export.addExport(this); + setExportMenuItemState(true); + } + + static addExport = (newExport: Export) => { + Export.exportsMap.set(newExport.createdAt, newExport); + }; + + get data() { + return { + defaultFileName: this.conversion.title, + status: this.status, + text: this.text, + percentage: this.percentage ?? 0, + image: this.conversion.video.previewImage?.data, + createdAt: this.createdAt, + filePath: this.conversion.convertedFilePath, + error: this.conversion.error, + disableOutputActions: this.disableOutputActions + }; + } + + disableActions = () => { + this.disableOutputActions = true; + }; + + start = async () => { + try { + this.process = this._start(); + await this.process; + } catch (error) { + if (!error.isCanceled) { + this.emit('error', error); + } + } + }; + + onProgress = (text: string, percentage: number) => { + this.emit('progress', {text, percentage}); + }; + + onCancel = () => { + this.process?.cancel(); + this.emit('canceled'); + }; +} + +export const setUpExportsListeners = () => { + ipc.answerRenderer('get-exports', () => Export.all.map(exp => exp.data)); + ipc.answerRenderer('cancel-export', (createdAt: number) => { + Export.exportsMap.get(createdAt)?.onCancel(); + }); + + ipc.answerRenderer('open-export', (createdAt: number) => { + Export.exportsMap.get(createdAt)?.conversion?.video?.openEditorWindow?.(); + }); + + ipcMain.on('drag-export', async (event: any, createdAt: number) => { + const conversion = Export.exportsMap.get(createdAt)?.conversion; + + if (conversion && (await conversion.filePathExists())) { + event.sender.startDrag({ + file: conversion.convertedFilePath, + icon: await conversion.video.getDragIcon(conversion.options) + }); + } + }); + + ipcMain.on('drag-conversion', async (event: any, id: string) => { + const conversion = Conversion.fromId(id); + + if (conversion && (await conversion.filePathExists())) { + event.sender.startDrag({ + file: conversion.convertedFilePath, + icon: await conversion.video.getDragIcon(conversion.options) + }); + } + }); +}; diff --git a/main/global-accelerators.js b/main/global-accelerators.ts similarity index 64% rename from main/global-accelerators.js rename to main/global-accelerators.ts index e3d9fe9d8..d7970beb8 100644 --- a/main/global-accelerators.js +++ b/main/global-accelerators.ts @@ -1,26 +1,25 @@ -'use strict'; -const {globalShortcut} = require('electron'); -const {ipcMain: ipc} = require('electron-better-ipc'); -const settings = require('./common/settings'); -const {openCropperWindow, isCropperOpen} = require('./cropper'); +import {globalShortcut} from 'electron'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import {settings} from './common/settings'; +import {windowManager} from './windows/manager'; const openCropper = () => { - if (!isCropperOpen()) { - openCropperWindow(); + if (!windowManager.cropper?.isOpen()) { + windowManager.cropper?.open(); } }; // All settings that should be loaded and handled as global accelerators -const handlers = new Map([ +const handlers = new Map void>([ ['triggerCropper', openCropper] ]); // If no action is passed, it resets -const setCropperShortcutAction = (action = openCropper) => { +export const setCropperShortcutAction = (action = openCropper) => { if (settings.get('enableShortcuts') && settings.get('shortcuts.triggerCropper')) { handlers.set('cropperShortcut', action); - const shortcut = settings.get('shortcuts.triggerCropper'); + const shortcut = settings.get('shortcuts.triggerCropper'); if (globalShortcut.isRegistered(shortcut)) { globalShortcut.unregister(shortcut); } @@ -29,7 +28,7 @@ const setCropperShortcutAction = (action = openCropper) => { } }; -const registerShortcut = (shortcut, action) => { +const registerShortcut = (shortcut: string, action: () => void) => { try { globalShortcut.register(shortcut, action); } catch (error) { @@ -40,7 +39,7 @@ const registerShortcut = (shortcut, action) => { const registerFromStore = () => { if (settings.get('enableShortcuts')) { for (const [setting, action] of handlers.entries()) { - const shortcut = settings.get(`shortcuts.${setting}`); + const shortcut = settings.get(`shortcuts.${setting}`); if (shortcut) { registerShortcut(shortcut, action); } @@ -50,9 +49,9 @@ const registerFromStore = () => { } }; -const initializeGlobalAccelerators = () => { +export const initializeGlobalAccelerators = () => { ipc.answerRenderer('update-shortcut', ({setting, shortcut}) => { - const oldShortcut = settings.get(`shortcuts.${setting}`); + const oldShortcut = settings.get(`shortcuts.${setting}`); try { if (oldShortcut && oldShortcut !== shortcut && globalShortcut.isRegistered(oldShortcut)) { @@ -63,11 +62,13 @@ const initializeGlobalAccelerators = () => { } finally { if (shortcut && shortcut !== oldShortcut) { settings.set(`shortcuts.${setting}`, shortcut); + const handler = handlers.get(setting); - if (settings.get('enableShortcuts')) { - registerShortcut(shortcut, handlers.get(setting)); + if (settings.get('enableShortcuts') && handler) { + registerShortcut(shortcut, handler); } } else if (!shortcut) { + // @ts-expect-error settings.delete(`shortcuts.${setting}`); } } @@ -84,8 +85,3 @@ const initializeGlobalAccelerators = () => { // Register keyboard shortcuts from store registerFromStore(); }; - -module.exports = { - initializeGlobalAccelerators, - setCropperShortcutAction -}; diff --git a/main/index.js b/main/index.ts similarity index 55% rename from main/index.js rename to main/index.ts index 89d450b90..9e41b4689 100644 --- a/main/index.js +++ b/main/index.ts @@ -1,32 +1,32 @@ -'use strict'; +import {app} from 'electron'; +import {is, enforceMacOSAppLocation} from 'electron-util'; +import log from 'electron-log'; +import {autoUpdater} from 'electron-updater'; +import toMilliseconds from '@sindresorhus/to-milliseconds'; + +import './windows/load'; +import './utils/sentry'; + +require('electron-timber').hookConsole({main: true, renderer: true}); + +import {settings} from './common/settings'; +import {plugins} from './plugins'; +import {initializeTray} from './tray'; +import {initializeDevices} from './utils/devices'; +import {setUpConversionListeners} from './conversion'; +import {initializeAnalytics, track} from './common/analytics'; +import {initializeGlobalAccelerators} from './global-accelerators'; +import {openFiles} from './utils/open-files'; +import {hasMicrophoneAccess, ensureScreenCapturePermissions} from './common/system-permissions'; +import {handleDeepLink} from './utils/deep-linking'; +import {hasActiveRecording, cleanPastRecordings} from './recording-history'; +import {setupRemoteStates} from './remote-states'; +import {setUpExportsListeners} from './export'; +import {windowManager} from './windows/manager'; -const {app} = require('electron'); const prepareNext = require('electron-next'); -const {is, enforceMacOSAppLocation} = require('electron-util'); -const log = require('electron-log'); -const {autoUpdater} = require('electron-updater'); -const toMilliseconds = require('@sindresorhus/to-milliseconds'); - -const settings = require('./common/settings'); - -require('./utils/sentry'); -require('./utils/errors').setupErrorHandling(); - -const {initializeTray} = require('./tray'); -const {initializeAnalytics} = require('./common/analytics'); -const initializeExportList = require('./export-list'); -const {openCropperWindow, isCropperOpen, closeAllCroppers} = require('./cropper'); -const {track} = require('./common/analytics'); -const plugins = require('./common/plugins'); -const {initializeGlobalAccelerators} = require('./global-accelerators'); -const {setApplicationMenu} = require('./menus'); -const openFiles = require('./utils/open-files'); -const {initializeExportOptions} = require('./export-options'); -const {hasMicrophoneAccess, ensureScreenCapturePermissions} = require('./common/system-permissions'); -const {handleDeepLink} = require('./utils/deep-linking'); -const {hasActiveRecording, cleanPastRecordings} = require('./recording-history'); - -const filesToOpen = []; + +const filesToOpen: string[] = []; app.commandLine.appendSwitch('--enable-features', 'OverlayScrollbar'); @@ -42,11 +42,8 @@ app.on('open-file', (event, path) => { }); const initializePlugins = async () => { - plugins.refreshRecordPluginServices(); - if (!is.development) { try { - await plugins.prune(); await plugins.upgrade(); } catch (error) { console.log(error); @@ -63,22 +60,29 @@ const checkForUpdates = () => { try { await autoUpdater.checkForUpdates(); } catch (error) { - autoUpdater.logger.error(error); + autoUpdater.logger?.error(error); } }; // For auto-update debugging in Console.app autoUpdater.logger = log; + // @ts-expect-error autoUpdater.logger.transports.file.level = 'info'; setInterval(checkForUpdates, toMilliseconds({hours: 1})); checkForUpdates(); + return true; }; // Prepare the renderer once the app is ready (async () => { await app.whenReady(); + require('./utils/errors').setupErrorHandling(); + + // Initialize remote states + setupRemoteStates(); + setUpConversionListeners(); app.dock.hide(); app.setAboutPanelOptions({copyright: 'Copyright © Wulkano'}); @@ -90,13 +94,11 @@ const checkForUpdates = () => { // Ensure all plugins are up to date initializePlugins(); - + initializeDevices(); initializeAnalytics(); initializeTray(); - initializeExportList(); initializeGlobalAccelerators(); - initializeExportOptions(); - setApplicationMenu(); + setUpExportsListeners(); if (!app.isDefaultProtocolClient('kap')) { app.setAsDefaultProtocolClient('kap'); @@ -112,25 +114,17 @@ const checkForUpdates = () => { ensureScreenCapturePermissions() && (!settings.get('recordAudio') || hasMicrophoneAccess()) ) { - openCropperWindow(); + windowManager.cropper?.open(); } checkForUpdates(); })(); -app.on('before-quit', closeAllCroppers); - -app.on('window-all-closed', event => { +app.on('window-all-closed', (event: any) => { app.dock.hide(); event.preventDefault(); }); -app.on('browser-window-created', () => { - if (!isCropperOpen()) { - app.dock.show(); - } -}); - app.on('will-finish-launching', () => { app.on('open-url', (event, url) => { event.preventDefault(); diff --git a/main/menus.js b/main/menus.js deleted file mode 100644 index 0f7ead6f3..000000000 --- a/main/menus.js +++ /dev/null @@ -1,277 +0,0 @@ -'use strict'; - -const {Menu, app, dialog, BrowserWindow} = require('electron'); -const {openNewGitHubIssue, appMenu} = require('electron-util'); -const {ipcMain: ipc} = require('electron-better-ipc'); -const delay = require('delay'); -const macosRelease = require('macos-release'); - -const {supportedVideoExtensions, defaultInputDeviceId} = require('./common/constants'); -const settings = require('./common/settings'); -const {hasMicrophoneAccess} = require('./common/system-permissions'); -const {getAudioDevices, getDefaultInputDevice} = require('./utils/devices'); -const {ensureDockIsShowing} = require('./utils/dock'); -const {openPrefsWindow} = require('./preferences'); -const {openExportsWindow} = require('./exports'); -const {closeAllCroppers} = require('./cropper'); -const {editorEmitter} = require('./editor'); -const openFiles = require('./utils/open-files'); - -const release = macosRelease(); -const issueBody = ` - - -**macOS version:** ${release.name} (${release.version}) -**Kap version:** ${app.getVersion()} - -#### Steps to reproduce - -#### Current behavior - -#### Expected behavior - -#### Workaround - - -`; - -const openFileItem = { - label: 'Open Video…', - accelerator: 'Command+O', - click: async () => { - closeAllCroppers(); - - await delay(200); - - await ensureDockIsShowing(async () => { - const {canceled, filePaths} = await dialog.showOpenDialog({ - filters: [{name: 'Videos', extensions: supportedVideoExtensions}], - properties: ['openFile', 'multiSelections'] - }); - - if (!canceled && filePaths) { - openFiles(...filePaths); - } - }); - } -}; - -const sendFeedbackItem = { - label: 'Send Feedback…', - click() { - openNewGitHubIssue({ - user: 'wulkano', - repo: 'kap', - body: issueBody - }); - } -}; - -const aboutItem = { - label: `About ${app.name}`, - click: () => { - closeAllCroppers(); - ensureDockIsShowing(() => { - app.showAboutPanel(); - }); - } -}; - -let isExportsItemEnabled = false; - -const getExportHistoryItem = () => ({ - label: 'Export History', - click: openExportsWindow, - enabled: isExportsItemEnabled, - id: 'exports' -}); - -const preferencesItem = { - label: 'Preferences…', - accelerator: 'Command+,', - click: () => openPrefsWindow() -}; - -let pluginsItems = []; - -const getPluginsItem = () => ({ - id: 'plugins', - label: 'Plugins', - submenu: pluginsItems, - visible: pluginsItems.length > 0 -}); - -const getMicrophoneItem = async () => { - const devices = await getAudioDevices(); - const isRecordAudioEnabled = settings.get('recordAudio'); - const currentDefaultDevice = getDefaultInputDevice(); - - let audioInputDeviceId = settings.get('audioInputDeviceId'); - if (!devices.some(device => device.id === audioInputDeviceId)) { - settings.set('audioInputDeviceId', defaultInputDeviceId); - audioInputDeviceId = defaultInputDeviceId; - } - - return { - id: 'devices', - label: 'Microphone', - submenu: [ - { - label: 'None', - type: 'checkbox', - checked: !isRecordAudioEnabled, - click: () => settings.set('recordAudio', false) - }, - ...[ - {name: `System Default${currentDefaultDevice ? ` (${currentDefaultDevice.name})` : ''}`, id: defaultInputDeviceId}, - ...devices - ].map(device => ({ - label: device.name, - type: 'checkbox', - checked: isRecordAudioEnabled && audioInputDeviceId === device.id, - click: () => { - settings.set('recordAudio', true); - settings.set('audioInputDeviceId', device.id); - } - })) - ], - visible: hasMicrophoneAccess() - }; -}; - -const getCogMenuTemplate = async () => [ - aboutItem, - { - type: 'separator' - }, - preferencesItem, - { - type: 'separator' - }, - getPluginsItem(), - await getMicrophoneItem(), - { - type: 'separator' - }, - openFileItem, - getExportHistoryItem(), - { - type: 'separator' - }, - sendFeedbackItem, - { - type: 'separator' - }, - { - role: 'quit', - accelerator: 'Command+Q' - } -]; - -const appMenuItem = appMenu([preferencesItem]); - -appMenuItem.submenu[0] = aboutItem; - -const appMenuTemplate = [ - appMenuItem, - { - role: 'fileMenu', - submenu: [ - openFileItem, - { - type: 'separator' - }, - { - label: 'Save Original…', - id: 'saveOriginal', - accelerator: 'Command+S', - click: () => { - ipc.callRenderer(BrowserWindow.getFocusedWindow(), 'save-original'); - } - }, - { - type: 'separator' - }, - { - role: 'close' - } - ] - }, - { - role: 'editMenu' - }, - { - role: 'windowMenu', - submenu: [ - { - role: 'minimize' - }, - { - role: 'zoom' - }, - { - type: 'separator' - }, - getExportHistoryItem(), - { - type: 'separator' - }, - { - role: 'front' - } - ] - }, - { - label: 'Help', - role: 'help', - submenu: [sendFeedbackItem] - } -]; - -const refreshRecordPluginItems = services => { - pluginsItems = services.map(service => ({ - label: service.title, - type: 'checkbox', - checked: service.isEnabled, - click: service.toggleEnabled - })); -}; - -const appMenu_ = Menu.buildFromTemplate(appMenuTemplate); -const appExportsItem = appMenu_.getMenuItemById('exports'); -const appSaveOriginalItem = appMenu_.getMenuItemById('saveOriginal'); - -const toggleExportMenuItem = enabled => { - isExportsItemEnabled = enabled; - appExportsItem.enabled = enabled; -}; - -const setAppMenu = () => { - Menu.setApplicationMenu(appMenu_); -}; - -editorEmitter.on('blur', () => { - appSaveOriginalItem.visible = false; -}); - -editorEmitter.on('focus', () => { - appSaveOriginalItem.visible = true; -}); - -const getCogMenu = async () => Menu.buildFromTemplate(await getCogMenuTemplate()); - -module.exports = { - getCogMenu, - toggleExportMenuItem, - setApplicationMenu: setAppMenu, - refreshRecordPluginItems -}; diff --git a/main/menus/application.ts b/main/menus/application.ts new file mode 100644 index 000000000..27b87b210 --- /dev/null +++ b/main/menus/application.ts @@ -0,0 +1,70 @@ +import {appMenu} from 'electron-util'; +import {getAboutMenuItem, getExportHistoryMenuItem, getOpenFileMenuItem, getPreferencesMenuItem, getSendFeedbackMenuItem} from './common'; +import {MenuItemId, MenuOptions} from './utils'; + +const getAppMenuItem = () => { + const appMenuItem = appMenu([getPreferencesMenuItem()]); + + // @ts-expect-error + appMenuItem.submenu[0] = getAboutMenuItem(); + return {...appMenuItem, id: MenuItemId.app}; +}; + +// eslint-disable-next-line unicorn/prevent-abbreviations +export const defaultApplicationMenu = (): MenuOptions => [ + getAppMenuItem(), + { + role: 'fileMenu', + id: MenuItemId.file, + submenu: [ + getOpenFileMenuItem(), + { + type: 'separator' + }, + { + role: 'close' + } + ] + }, + { + role: 'editMenu', + id: MenuItemId.edit + }, + { + role: 'windowMenu', + id: MenuItemId.window, + submenu: [ + { + role: 'minimize' + }, + { + role: 'zoom' + }, + { + type: 'separator' + }, + getExportHistoryMenuItem(), + { + type: 'separator' + }, + { + role: 'front' + } + ] + }, + { + id: MenuItemId.help, + label: 'Help', + role: 'help', + submenu: [getSendFeedbackMenuItem()] + } +]; + +// eslint-disable-next-line unicorn/prevent-abbreviations +export const customApplicationMenu = (modifier: (defaultMenu: ReturnType) => void) => { + const menu = defaultApplicationMenu(); + modifier(menu); + return menu; +}; + +export type MenuModifier = Parameters[0]; diff --git a/main/menus/cog.ts b/main/menus/cog.ts new file mode 100644 index 000000000..4c80bef1f --- /dev/null +++ b/main/menus/cog.ts @@ -0,0 +1,101 @@ +import {Menu} from 'electron'; +import {MenuItemId, MenuOptions} from './utils'; +import {getAboutMenuItem, getExportHistoryMenuItem, getOpenFileMenuItem, getPreferencesMenuItem, getSendFeedbackMenuItem} from './common'; +import {plugins} from '../plugins'; +import {getAudioDevices, getDefaultInputDevice} from '../utils/devices'; +import {settings} from '../common/settings'; +import {defaultInputDeviceId} from '../common/constants'; +import {hasMicrophoneAccess} from '../common/system-permissions'; + +const getCogMenuTemplate = async (): Promise => [ + getAboutMenuItem(), + { + type: 'separator' + }, + getPreferencesMenuItem(), + { + type: 'separator' + }, + getPluginsItem(), + await getMicrophoneItem(), + { + type: 'separator' + }, + getOpenFileMenuItem(), + getExportHistoryMenuItem(), + { + type: 'separator' + }, + getSendFeedbackMenuItem(), + { + type: 'separator' + }, + { + role: 'quit', + accelerator: 'Command+Q' + } +]; + +const getPluginsItem = (): MenuOptions[number] => { + const items = plugins.recordingPlugins.flatMap(plugin => + plugin.recordServicesWithStatus.map(service => ({ + label: service.title, + type: 'checkbox' as const, + checked: service.isEnabled, + click: async () => service.setEnabled(!service.isEnabled) + })) + ); + + return { + id: MenuItemId.plugins, + label: 'Plugins', + submenu: items, + visible: items.length > 0 + }; +}; + +const getMicrophoneItem = async (): Promise => { + const devices = await getAudioDevices(); + const isRecordAudioEnabled = settings.get('recordAudio'); + const currentDefaultDevice = getDefaultInputDevice(); + + let audioInputDeviceId = settings.get('audioInputDeviceId'); + if (!devices.some(device => device.id === audioInputDeviceId)) { + settings.set('audioInputDeviceId', defaultInputDeviceId); + audioInputDeviceId = defaultInputDeviceId; + } + + return { + id: MenuItemId.audioDevices, + label: 'Microphone', + submenu: [ + { + label: 'None', + type: 'checkbox', + checked: !isRecordAudioEnabled, + click: () => { + settings.set('recordAudio', false); + } + }, + ...[ + {name: `System Default${currentDefaultDevice ? ` (${currentDefaultDevice.name})` : ''}`, id: defaultInputDeviceId}, + ...devices + ].map(device => ({ + label: device.name, + type: 'checkbox' as const, + checked: isRecordAudioEnabled && (audioInputDeviceId === device.id), + click: () => { + settings.set('recordAudio', true); + settings.set('audioInputDeviceId', device.id); + } + })) + ], + visible: hasMicrophoneAccess() + }; +}; + +export const getCogMenu = async () => { + return Menu.buildFromTemplate( + await getCogMenuTemplate() + ); +}; diff --git a/main/menus/common.ts b/main/menus/common.ts new file mode 100644 index 000000000..aa8cdad51 --- /dev/null +++ b/main/menus/common.ts @@ -0,0 +1,94 @@ +import delay from 'delay'; +import {app, dialog} from 'electron'; +import {openNewGitHubIssue} from 'electron-util'; +import macosRelease from 'macos-release'; +import {supportedVideoExtensions} from '../common/constants'; +import {getCurrentMenuItem, MenuItemId} from './utils'; +import {openFiles} from '../utils/open-files'; +import {windowManager} from '../windows/manager'; + +export const getPreferencesMenuItem = () => ({ + id: MenuItemId.preferences, + label: 'Preferences…', + accelerator: 'Command+,', + click: () => windowManager.preferences?.open() +}); + +export const getAboutMenuItem = () => ({ + id: MenuItemId.about, + label: `About ${app.name}`, + click: () => { + windowManager.cropper?.close(); + app.focus(); + app.showAboutPanel(); + } +}); + +export const getOpenFileMenuItem = () => ({ + id: MenuItemId.openVideo, + label: 'Open Video…', + accelerator: 'Command+O', + click: async () => { + windowManager.cropper?.close(); + + await delay(200); + + app.focus(); + const {canceled, filePaths} = await dialog.showOpenDialog({ + filters: [{name: 'Videos', extensions: supportedVideoExtensions}], + properties: ['openFile', 'multiSelections'] + }); + + if (!canceled && filePaths) { + openFiles(...filePaths); + } + } +}); + +export const getExportHistoryMenuItem = () => ({ + label: 'Export History', + click: () => windowManager.exports?.open(), + enabled: getCurrentMenuItem(MenuItemId.exportHistory)?.enabled ?? false, + id: MenuItemId.exportHistory +}); + +export const getSendFeedbackMenuItem = () => ({ + id: MenuItemId.sendFeedback, + label: 'Send Feedback…', + click() { + openNewGitHubIssue({ + user: 'wulkano', + repo: 'kap', + body: issueBody + }); + } +}); + +const release = macosRelease(); + +const issueBody = ` + + +**macOS version:** ${release.name} (${release.version}) +**Kap version:** ${app.getVersion()} + +#### Steps to reproduce + +#### Current behavior + +#### Expected behavior + +#### Workaround + + +`; + diff --git a/main/menus/utils.ts b/main/menus/utils.ts new file mode 100644 index 000000000..34324fa87 --- /dev/null +++ b/main/menus/utils.ts @@ -0,0 +1,31 @@ +import {Menu} from 'electron'; + +export type MenuOptions = Parameters[0]; + +export enum MenuItemId { + exportHistory = 'exportHistory', + sendFeedback = 'sendFeedback', + openVideo = 'openVideo', + about = 'about', + preferences = 'preferences', + file = 'file', + edit = 'edit', + window = 'window', + help = 'help', + app = 'app', + saveOriginal = 'saveOriginal', + plugins = 'plugins', + audioDevices = 'audioDevices' +} + +export const getCurrentMenuItem = (id: MenuItemId) => { + return Menu.getApplicationMenu()?.getMenuItemById(id); +}; + +export const setExportMenuItemState = (enabled: boolean) => { + const menuItem = Menu.getApplicationMenu()?.getMenuItemById(MenuItemId.exportHistory); + + if (menuItem) { + menuItem.enabled = enabled; + } +}; diff --git a/main/plugin.js b/main/plugin.js deleted file mode 100644 index 780c6ba0f..000000000 --- a/main/plugin.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; - -const path = require('path'); -const fs = require('fs'); -const electron = require('electron'); -const semver = require('semver'); -const Store = require('electron-store'); -const readPkg = require('read-pkg'); -const macosVersion = require('macos-version'); - -const PluginConfig = require('./utils/plugin-config'); -const {showError} = require('./utils/errors'); - -const {app, shell} = electron; - -const recordPluginServiceState = new Store({ - name: 'record-plugin-state', - defaults: {} -}); - -class BasePlugin { - constructor(pluginName) { - this.name = pluginName; - } - - get prettyName() { - return this.name.replace(/^kap-/, ''); - } - - get isCompatible() { - return semver.satisfies(app.getVersion(), this.kapVersion || '*') && macosVersion.is(this.macosVersion || '*'); - } - - get repoUrl() { - const url = new URL(this.link); - url.hash = ''; - return url.href; - } -} - -class InstalledPlugin extends BasePlugin { - constructor(pluginName) { - super(pluginName); - - this.isInstalled = true; - this.cwd = path.join(app.getPath('userData'), 'plugins'); - this.pkgPath = path.join(this.cwd, 'package.json'); - this.isSymlink = fs.lstatSync(this.pluginPath).isSymbolicLink(); - - this.json = readPkg.sync({cwd: this.pluginPath}); - - const {homepage, links, kap = {}} = this.json; - this.link = homepage || (links && links.homepage); - - // Keeping for backwards compatibility - this.kapVersion = kap.version || this.json.kapVersion; - this.macosVersion = kap.macosVersion; - - try { - this.plugin = require(this.pluginPath); - this.config = new PluginConfig(this); - - if (this.plugin.didConfigChange && typeof this.plugin.didConfigChange === 'function') { - this.config.onDidAnyChange((newValue, oldValue) => this.plugin.didConfigChange(newValue, oldValue, this.config)); - } - } catch (error) { - showError(error, {title: `Something went wrong while loading “${pluginName}”`, plugin: this}); - this.plugin = {}; - } - } - - getPath(subPath = '') { - return path.join(this.cwd, 'node_modules', this.name, subPath); - } - - get version() { - return this.json.version; - } - - get description() { - return this.json.description; - } - - get pluginPath() { - return this.getPath(); - } - - get isValid() { - return this.config.isConfigValid(); - } - - get hasConfig() { - return this.allServices.some(({config = {}}) => Object.keys(config).length > 0); - } - - get recordServices() { - return this.plugin.recordServices || []; - } - - get recordServicesWithStatus() { - return this.recordServices.map(service => ({ - ...service, - isEnabled: recordPluginServiceState.get(service.title) || false - })); - } - - get shareServices() { - return this.plugin.shareServices || []; - } - - get editServices() { - return this.plugin.editServices || []; - } - - get allServices() { - return [ - ...this.recordServices, - ...this.shareServices, - ...this.editServices - ]; - } - - openConfig() { - this.config.openInEditor(); - } - - viewOnGithub() { - shell.openExternal(this.link); - } -} - -class NpmPlugin extends BasePlugin { - constructor(json, kap = {}) { - super(json.name); - - this.kapVersion = kap.version; - this.macosVersion = kap.macosVersion; - this.isInstalled = false; - this.json = json; - - this.version = json.version; - this.description = json.description; - - const {homepage, links} = json; - this.link = homepage || (links && links.homepage); - } - - viewOnGithub() { - shell.openExternal(this.link); - } -} - -module.exports = { - InstalledPlugin, - NpmPlugin, - recordPluginServiceState -}; diff --git a/main/plugins/copy-to-clipboard-plugin.js b/main/plugins/built-in/copy-to-clipboard-plugin.ts similarity index 60% rename from main/plugins/copy-to-clipboard-plugin.js rename to main/plugins/built-in/copy-to-clipboard-plugin.ts index 499fec14b..16801bf69 100644 --- a/main/plugins/copy-to-clipboard-plugin.js +++ b/main/plugins/built-in/copy-to-clipboard-plugin.ts @@ -1,12 +1,13 @@ -'use strict'; -const {clipboard} = require('electron'); +import {clipboard} from 'electron'; +import {ShareServiceContext} from '../service-context'; + const plist = require('plist'); -const copyFileReferencesToClipboard = filePaths => { +const copyFileReferencesToClipboard = (filePaths: string[]) => { clipboard.writeBuffer('NSFilenamesPboardType', Buffer.from(plist.build(filePaths))); }; -const action = async context => { +const action = async (context: ShareServiceContext) => { const filePath = await context.filePath(); copyFileReferencesToClipboard([filePath]); context.notify(`The ${context.prettyFormat} has been copied to the clipboard`); @@ -21,4 +22,4 @@ const copyToClipboard = { action }; -module.exports.shareServices = [copyToClipboard]; +export const shareServices = [copyToClipboard]; diff --git a/main/plugins/built-in/open-with-plugin.ts b/main/plugins/built-in/open-with-plugin.ts new file mode 100644 index 000000000..bdf8919c8 --- /dev/null +++ b/main/plugins/built-in/open-with-plugin.ts @@ -0,0 +1,47 @@ +import {ShareServiceContext} from '../service-context'; +import path from 'path'; +import {getFormatExtension} from '../../common/constants'; +import {Format} from '../../common/types'; + +const {getAppsThatOpenExtension, openFileWithApp} = require('mac-open-with'); + +const action = async (context: ShareServiceContext & {appUrl: string}) => { + const filePath = await context.filePath(); + openFileWithApp(filePath, context.appUrl); +}; + +export interface App { + url: string; + isDefault: boolean; + icon: string; + name: string; +} + +const getAppsForFormat = (format: Format) => { + return (getAppsThatOpenExtension.sync(getFormatExtension(format)) as App[]) + .map(app => ({...app, name: decodeURI(path.parse(app.url).name)})) + .filter(app => !['Kap', 'Kap Beta'].includes(app.name)) + .sort((a, b) => { + if (a.isDefault !== b.isDefault) { + return Number(b.isDefault) - Number(a.isDefault); + } + + return Number(b.name === 'Gifski') - Number(a.name === 'Gifski'); + }); +}; + +const appsForFormat = (['mp4', 'gif', 'apng', 'webm', 'av1'] as Format[]) + .map(format => ({ + format, + apps: getAppsForFormat(format) + })) + .filter(({apps}) => apps.length > 0); + +export const apps = new Map(appsForFormat.map(({format, apps}) => [format, apps])); + +export const shareServices = [{ + title: 'Open With', + formats: [...apps.keys()], + action +}]; + diff --git a/main/plugins/built-in/save-file-plugin.ts b/main/plugins/built-in/save-file-plugin.ts new file mode 100644 index 000000000..53da44341 --- /dev/null +++ b/main/plugins/built-in/save-file-plugin.ts @@ -0,0 +1,86 @@ +'use strict'; + +import {BrowserWindow, dialog} from 'electron'; +import {ShareServiceContext} from '../service-context'; +import {settings} from '../../common/settings'; +import makeDir from 'make-dir'; +import {Format} from '../../common/types'; +import path from 'path'; + +const {Notification, shell} = require('electron'); +const cpFile = require('cp-file'); + +const action = async (context: ShareServiceContext & {targetFilePath: string}) => { + const temporaryFilePath = await context.filePath(); + + // Execution has been interrupted + if (context.isCanceled) { + return; + } + + // Copy the file, so we can still use the temporary source for future exports + // The temporary file will be cleaned up on app exit, or automatic system cleanup + await cpFile(temporaryFilePath, context.targetFilePath); + + const notification = new Notification({ + title: 'File saved successfully!', + body: 'Click to show the file in Finder' + }); + + notification.on('click', () => { + shell.showItemInFolder(context.targetFilePath); + }); + + notification.show(); +}; + +const saveFile = { + title: 'Save to Disk', + formats: [ + 'gif', + 'mp4', + 'webm', + 'apng', + 'av1' + ], + action +}; + +export const shareServices = [saveFile]; + +const filterMap = new Map([ + [Format.mp4, [{name: 'Movies', extensions: ['mp4']}]], + [Format.webm, [{name: 'Movies', extensions: ['webm']}]], + [Format.gif, [{name: 'Images', extensions: ['gif']}]], + [Format.apng, [{name: 'Images', extensions: ['apng']}]], + [Format.av1, [{name: 'Movies', extensions: ['mp4']}]] +]); + +let lastSavedDirectory: string; + +export const askForTargetFilePath = async ( + window: BrowserWindow, + format: Format, + fileName: string +) => { + const kapturesDir = settings.get('kapturesDir'); + await makeDir(kapturesDir); + + const defaultPath = path.join(lastSavedDirectory ?? kapturesDir, fileName); + + const filters = filterMap.get(format); + + const {filePath} = await dialog.showSaveDialog(window, { + title: fileName, + defaultPath, + filters + }); + + if (filePath) { + lastSavedDirectory = path.dirname(filePath); + return filePath; + } + + return undefined; +}; + diff --git a/main/plugins/config.ts b/main/plugins/config.ts new file mode 100644 index 000000000..78b5923ca --- /dev/null +++ b/main/plugins/config.ts @@ -0,0 +1,81 @@ +import {ValidateFunction} from 'ajv'; +import Store, {Schema as JSONSchema} from 'electron-store'; +import Ajv, {Schema} from '../utils/ajv'; +import {Service} from './service'; + +export default class PluginConfig extends Store { + servicesWithNoConfig: Service[]; + validators: Array<{ + title: string; + description?: string; + config: Record; + validate: ValidateFunction; + }>; + + constructor(name: string, services: Service[]) { + const defaults = {}; + + const validators = services + .filter(({config}) => Boolean(config)) + .map(service => { + const config = service.config as Record; + const schema: Record> = {}; + const requiredKeys = []; + + for (const key of Object.keys(config)) { + if (!config[key].title) { + throw new Error('Config schema items should have a `title`'); + } + + const {required, ...rest} = config[key]; + + if (required) { + requiredKeys.push(key); + } + + schema[key] = rest; + } + + const ajv = new Ajv({ + format: 'full', + useDefaults: true, + errorDataPath: 'property', + allErrors: true + }); + + const validator = ajv.compile({ + type: 'object', + properties: schema, + required: requiredKeys + }); + + validator(defaults); + return { + validate: validator, + title: service.title, + description: service.configDescription, + config + }; + }); + + super({ + name, + cwd: 'plugins', + defaults + }); + + this.servicesWithNoConfig = services.filter(({config}) => !config); + this.validators = validators; + } + + get isValid() { + return this.validators.every(validator => validator.validate(this.store)); + } + + get validServices() { + return [ + ...this.validators.filter(validator => validator.validate(this.store)), + ...this.servicesWithNoConfig + ].map(service => service.title); + } +} diff --git a/main/plugins/index.ts b/main/plugins/index.ts new file mode 100644 index 000000000..f95e884d2 --- /dev/null +++ b/main/plugins/index.ts @@ -0,0 +1,216 @@ +import {app} from 'electron'; +import {EventEmitter} from 'events'; +import path from 'path'; +import fs from 'fs'; +import makeDir from 'make-dir'; +import execa from 'execa'; +import {track} from '../common/analytics'; +import {InstalledPlugin, NpmPlugin} from './plugin'; +import {showError} from '../utils/errors'; +import {notify} from '../utils/notifications'; +import packageJson from 'package-json'; +import {NormalizedPackageJson} from 'read-pkg'; +import {windowManager} from '../windows/manager'; + +const got = require('got'); + +type PackageJson = { + dependencies: Record; +}; + +export class Plugins extends EventEmitter { + yarnBin = path.join(__dirname, '../../node_modules/yarn/bin/yarn.js'); + appVersion = app.getVersion(); + pluginsDir = path.join(app.getPath('userData'), 'plugins'); + builtInDir = path.join(__dirname, 'built-in'); + packageJsonPath = path.join(this.pluginsDir, 'package.json'); + installedPlugins: InstalledPlugin[] = []; + builtInPlugins = [ + new InstalledPlugin('_copyToClipboard', path.resolve(this.builtInDir, 'copy-to-clipboard-plugin')), + new InstalledPlugin('_saveToDisk', path.resolve(this.builtInDir, 'save-file-plugin')), + new InstalledPlugin('_openWith', path.resolve(this.builtInDir, 'open-with-plugin')) + ]; + + constructor() { + super(); + this.makePluginsDir(); + this.loadPlugins(); + } + + async install(name: string): Promise { + track(`plugin/installed/${name}`); + + this.modifyMainPackageJson(pkg => { + if (!pkg.dependencies) { + pkg.dependencies = {}; + } + + pkg.dependencies[name] = 'latest'; + }); + + try { + await this.yarnInstall(); + + const plugin = new InstalledPlugin(name); + this.installedPlugins.push(plugin); + + if (plugin.content.didInstall && typeof plugin.content.didInstall === 'function') { + try { + await plugin.content.didInstall?.(plugin.config); + } catch (error) { + showError(error, {plugin} as any); + } + } + + const {isValid, hasConfig} = plugin; + + // Const openConfig = () => openPrefsWindow({target: {name, action: 'configure'}}); + const openConfig = () => plugin.openConfig(); + + const options = (isValid && !hasConfig) ? { + title: 'Plugin installed', + body: `"${plugin.prettyName}" is ready for use` + } : { + title: plugin.isValid ? 'Plugin installed' : 'Configure plugin', + body: `"${plugin.prettyName}" ${plugin.isValid ? 'can be configured' : 'requires configuration'}`, + click: openConfig, + actions: [ + {type: 'button' as const, text: 'Configure', action: openConfig}, + {type: 'button' as const, text: 'Later'} + ] + }; + + notify(options); + + const validServices = plugin.config.validServices; + + for (const service of plugin.recordServices) { + if (!service.willEnable && validServices.includes(service.title)) { + plugin.enableService(service); + } + } + + this.emit('installed', plugin); + return plugin; + } catch (error) { + notify.simple(`Something went wrong while installing ${name}`); + this.modifyMainPackageJson(pkg => { + delete pkg.dependencies[name]; + }); + showError(error); + } + } + + async uninstall(name: string) { + track(`plugin/uninstalled/${name}`); + this.modifyMainPackageJson(pkg => { + delete pkg.dependencies[name]; + }); + const plugin = new InstalledPlugin(name); + + if (plugin.content.willUninstall && typeof plugin.content.willUninstall === 'function') { + try { + await plugin.content.willUninstall?.(plugin.config); + } catch (error) { + showError(error, {plugin} as any); + } + } + + this.installedPlugins = this.installedPlugins.filter(plugin => plugin.name !== name); + plugin.config.clear(); + this.emit('uninstalled', name); + + const json = plugin.json!; + + return new NpmPlugin(json, { + version: json.kapVersion, + ...json.kap + }); + } + + async upgrade() { + return this.yarnInstall(); + } + + async getFromNpm() { + const url = 'https://api.npms.io/v2/search?q=keywords:kap-plugin+not:deprecated'; + const response = (await got(url, {json: true})) as { + body: {results: Array<{package: NormalizedPackageJson}>}; + }; + const installed = this.pluginNames; + + return Promise.all(response.body.results + .map(x => x.package) + .filter(x => x.name.startsWith('kap-')) + .filter(x => !installed.includes(x.name)) // Filter out installed plugins + .map(async x => { + const {kap, kapVersion} = await packageJson(x.name, {fullMetadata: true}) as any; + return new NpmPlugin(x, { + // Keeping for backwards compatibility + version: kapVersion, + ...kap + }); + })); + } + + get allPlugins() { + return [ + ...this.installedPlugins, + ...this.builtInPlugins + ]; + } + + get sharePlugins() { + return this.allPlugins.filter(plugin => plugin.shareServices.length > 0); + } + + get editPlugins() { + return this.allPlugins.filter(plugin => plugin.editServices.length > 0); + } + + get recordingPlugins() { + return this.allPlugins.filter(plugin => plugin.recordServices.length > 0); + } + + openPluginConfig = async (pluginName: string) => { + return windowManager.config?.open(pluginName); + }; + + private makePluginsDir() { + if (!fs.existsSync(this.packageJsonPath)) { + makeDir.sync(this.pluginsDir); + fs.writeFileSync(this.packageJsonPath, JSON.stringify({dependencies: {}}, null, 2)); + } + } + + private modifyMainPackageJson(modifier: (pkg: PackageJson) => void) { + const pkg = JSON.parse(fs.readFileSync(this.packageJsonPath, 'utf8')); + modifier(pkg); + fs.writeFileSync(this.packageJsonPath, JSON.stringify(pkg, null, 2)); + } + + private async runYarn(...args: string[]) { + await execa(process.execPath, [this.yarnBin, ...args], { + cwd: this.pluginsDir, + env: { + ELECTRON_RUN_AS_NODE: '1', + NODE_ENV: 'development' + } + }); + } + + private get pluginNames() { + const pkg = fs.readFileSync(this.packageJsonPath, 'utf8'); + return Object.keys(JSON.parse(pkg).dependencies || {}); + } + + private async yarnInstall() { + await this.runYarn('install', '--no-lockfile', '--registry', 'https://registry.npmjs.org'); + } + + private loadPlugins() { + this.installedPlugins = this.pluginNames.map(name => new InstalledPlugin(name)); + } +} + +export const plugins = new Plugins(); diff --git a/main/plugins/open-with-plugin.js b/main/plugins/open-with-plugin.js deleted file mode 100644 index e07ceb9ce..000000000 --- a/main/plugins/open-with-plugin.js +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; -const path = require('path'); -const {getAppsThatOpenExtension, openFileWithApp} = require('mac-open-with'); -const {getFormatExtension} = require('../common/constants'); - -const action = async context => { - const filePath = await context.filePath(); - openFileWithApp(filePath, context.appUrl); -}; - -const apps = new Map( - ['mp4', 'gif', 'apng', 'webm', 'av1'] - .map(format => [ - format, - getAppsThatOpenExtension.sync(getFormatExtension(format)) - .map(app => ({ - ...app, - name: decodeURI(path.parse(app.url).name) - })) - .filter(app => !['Kap', 'Kap Beta'].includes(app.name)) - .sort((a, b) => { - if (a.isDefault !== b.isDefault) { - return b.isDefault - a.isDefault; - } - - return (b.name === 'Gifski') - (a.name === 'Gifski'); - }) - ]) - .filter(([_, apps]) => apps.length > 0) -); - -module.exports.shareServices = [{ - title: 'Open With', - formats: [...apps.keys()], - action -}]; - -module.exports.apps = apps; diff --git a/main/plugins/plugin.ts b/main/plugins/plugin.ts new file mode 100644 index 000000000..aaf5f1253 --- /dev/null +++ b/main/plugins/plugin.ts @@ -0,0 +1,208 @@ +import {app, shell} from 'electron'; +import macosVersion from 'macos-version'; +import semver from 'semver'; +import path from 'path'; +import fs from 'fs'; +import readPkg from 'read-pkg'; +import {RecordService, ShareService, EditService} from './service'; +import {showError} from '../utils/errors'; +import PluginConfig from './config'; +import Store from 'electron-store'; +import {windowManager} from '../windows/manager'; + +export const recordPluginServiceState = new Store>({ + name: 'record-plugin-state', + defaults: {} +}); + +class BasePlugin { + name: string; + kapVersion?: string; + macosVersion?: string; + link?: string; + json?: readPkg.NormalizedPackageJson; + + constructor(pluginName: string) { + this.name = pluginName; + } + + get prettyName() { + return this.name.replace(/^kap-/, ''); + } + + get isCompatible() { + return semver.satisfies(app.getVersion(), this.kapVersion ?? '*') && macosVersion.is(this.macosVersion ?? '*'); + } + + get repoUrl() { + if (!this.link) { + return ''; + } + + const url = new URL(this.link); + url.hash = ''; + return url.href; + } + + get version() { + return this.json?.version; + } + + get description() { + return this.json?.description; + } + + viewOnGithub() { + if (this.link) { + shell.openExternal(this.link); + } + } +} + +export interface KapPlugin { + shareServices?: Array>; + editServices?: Array>; + recordServices?: Array>; + + didConfigChange?: (newValue: Readonly | undefined, oldValue: Readonly | undefined, config: Store) => void | Promise; + didInstall?: (config: Store) => void | Promise; + willUninstall?: (config: Store) => void | Promise; +} + +export class InstalledPlugin extends BasePlugin { + isInstalled = true; + pluginsPath = path.join(app.getPath('userData'), 'plugins'); + + pluginPath: string; + json?: readPkg.NormalizedPackageJson; + content: KapPlugin; + config: PluginConfig; + hasConfig: boolean; + isBuiltIn: boolean; + + constructor(pluginName: string, customPath?: string) { + super(pluginName); + + this.pluginPath = customPath ?? path.join(this.pluginsPath, 'node_modules', pluginName); + this.isBuiltIn = Boolean(customPath); + + if (!this.isBuiltIn) { + this.json = readPkg.sync({cwd: this.pluginPath}); + this.link = this.json.homepage ?? this.json.links?.homepage; + + // Keeping for backwards compatibility + this.kapVersion = this.json.kap?.version ?? this.json.kapVersion; + this.macosVersion = this.json.kap?.macosVersion; + } + + try { + this.content = require(this.pluginPath); + this.config = new PluginConfig(pluginName, this.allServices); + this.hasConfig = this.allServices.some(({config = {}}) => Object.keys(config).length > 0); + + if (this.content.didConfigChange && typeof this.content.didConfigChange === 'function') { + this.config.onDidAnyChange((newValue, oldValue) => this.content.didConfigChange?.(newValue, oldValue, this.config)); + } + } catch (error) { + showError(error, {title: `Something went wrong while loading “${pluginName}”`, plugin: this}); + + this.content = {}; + this.config = new PluginConfig(pluginName, []); + this.hasConfig = false; + } + } + + get isSymLink() { + return fs.lstatSync(this.pluginPath).isSymbolicLink(); + } + + get shareServices() { + return this.content.shareServices ?? []; + } + + get editServices() { + return this.content.editServices ?? []; + } + + get recordServices() { + return this.content.recordServices ?? []; + } + + get allServices() { + return [ + ...this.shareServices, + ...this.editServices, + ...this.recordServices + ]; + } + + get isValid() { + return this.config.isValid; + } + + get recordServicesWithStatus() { + return this.recordServices.map(service => ({ + ...service, + isEnabled: recordPluginServiceState.get(this.getRecordServiceKey(service), false), + setEnabled: this.getSetEnableFunction(service) + })); + } + + enableService = (service: RecordService) => { + recordPluginServiceState.set(this.getRecordServiceKey(service), true); + }; + + openConfig = () => windowManager.config?.open(this.name); + + openConfigInEditor = () => { + // TODO: replace with `this.config.openInEditor()` when we upgrade to an Electron version that has `shell.openPath()` + shell.openItem(this.config.path); + }; + + private readonly getSetEnableFunction = (service: RecordService) => async (enabled: boolean) => { + const isEnabled = recordPluginServiceState.get(this.getRecordServiceKey(service), false); + + if (isEnabled === enabled) { + return; + } + + if (!enabled) { + recordPluginServiceState.set(this.getRecordServiceKey(service), false); + return; + } + + if (!this.config.validServices.includes(service.title)) { + windowManager.preferences?.open({target: {name: this.name, action: 'configure'}}); + return; + } + + if (service.willEnable && typeof service.willEnable === 'function') { + try { + const canEnable = await service.willEnable(); + + if (canEnable) { + recordPluginServiceState.set(this.getRecordServiceKey(service), true); + } + } catch (error) { + showError(error, {title: `Something went wrong while enabling "${service.title}`, plugin: this}); + } + } else { + recordPluginServiceState.set(this.getRecordServiceKey(service), true); + } + }; + + private readonly getRecordServiceKey = (service: RecordService) => `${this.name}-${service.title}`; +} + +export class NpmPlugin extends BasePlugin { + isInstalled = false; + + constructor(json: readPkg.NormalizedPackageJson, kap: {version?: string; macosVersion?: string} = {}) { + super(json.name); + + this.json = json; + this.kapVersion = kap.version; + this.macosVersion = kap.macosVersion; + this.link = this.json.homepage ?? this.json.links?.homepage; + } +} diff --git a/main/plugins/save-file-plugin.js b/main/plugins/save-file-plugin.js deleted file mode 100644 index c8a0db168..000000000 --- a/main/plugins/save-file-plugin.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const {Notification, shell} = require('electron'); -const moveFile = require('move-file'); - -const action = async context => { - const temporaryFilePath = await context.filePath(); - - // Execution has been interrupted - if (context.canceled) { - return; - } - - await moveFile(temporaryFilePath, context.targetFilePath); - - const notification = new Notification({ - title: 'File saved successfully!', - body: 'Click to show the file in Finder' - }); - - notification.on('click', () => { - shell.showItemInFolder(context.targetFilePath); - }); - - notification.show(); -}; - -const saveFile = { - title: 'Save to Disk', - formats: [ - 'gif', - 'mp4', - 'webm', - 'apng', - 'av1' - ], - action -}; - -module.exports.shareServices = [saveFile]; diff --git a/main/plugins/service-context.ts b/main/plugins/service-context.ts new file mode 100644 index 000000000..feac75094 --- /dev/null +++ b/main/plugins/service-context.ts @@ -0,0 +1,190 @@ +import {app, clipboard} from 'electron'; +import Store from 'electron-store'; +import got, {GotFn, GotPromise} from 'got'; +import {ApertureOptions, Format} from '../common/types'; +import {InstalledPlugin} from './plugin'; +import {addPluginPromise} from '../utils/deep-linking'; +import {notify} from '../utils/notifications'; +import PCancelable from 'p-cancelable'; +import {getFormatExtension} from '../common/constants'; + +interface ServiceContextOptions { + plugin: InstalledPlugin; +} + +class ServiceContext { + requests: Array> = []; + config: Store; + + private readonly plugin: InstalledPlugin; + + constructor(options: ServiceContextOptions) { + this.plugin = options.plugin; + this.config = this.plugin.config; + } + + request = (...args: Parameters) => { + const request = got(...args); + this.requests.push(request); + return request; + }; + + copyToClipboard = (text: string) => { + clipboard.writeText(text); + }; + + notify = (text: string, action?: () => any) => { + return notify({ + body: text, + title: this.plugin.isBuiltIn ? app.name : this.plugin.prettyName, + click: action + }); + }; + + openConfigFile = () => { + this.config.openInEditor(); + }; + + waitForDeepLink = async () => { + return new Promise(resolve => { + addPluginPromise(this.plugin.name, resolve); + }); + }; +} + +interface ShareServiceContextOptions extends ServiceContextOptions { + onProgress: (text: string, percentage: number) => void; + filePath: (options?: {fileType?: Format}) => Promise; + format: Format; + prettyFormat: string; + defaultFileName: string; + onCancel: () => void; +} + +export class ShareServiceContext extends ServiceContext { + isCanceled = false; + + private readonly options: ShareServiceContextOptions; + + constructor(options: ShareServiceContextOptions) { + super(options); + this.options = options; + } + + get format() { + return this.options.format; + } + + get prettyFormat() { + return this.options.prettyFormat; + } + + get defaultFileName() { + return `${this.options.defaultFileName}.${getFormatExtension(this.options.format)}`; + } + + filePath = async (options?: {fileType?: Format}) => { + return this.options.filePath(options); + }; + + setProgress = (text: string, percentage: number) => { + this.options.onProgress(text, percentage); + }; + + cancel = () => { + this.isCanceled = true; + this.options.onCancel(); + + for (const request of this.requests) { + request.cancel(); + } + }; +} + +interface EditServiceContextOptions extends ServiceContextOptions { + onProgress: (text: string, percentage: number) => void; + inputPath: string; + outputPath: string; + exportOptions: { + width: number; + height: number; + format: Format; + fps: number; + duration: number; + isMuted: boolean; + loop: boolean; + }; + convert: (args: string[], text?: string) => PCancelable; + onCancel: () => void; +} + +export class EditServiceContext extends ServiceContext { + isCanceled = false; + + private readonly options: EditServiceContextOptions; + + constructor(options: EditServiceContextOptions) { + super(options); + this.options = options; + } + + get inputPath() { + return this.options.inputPath; + } + + get outputPath() { + return this.options.outputPath; + } + + get exportOptions() { + return this.options.exportOptions; + } + + get convert() { + return this.options.convert; + } + + setProgress = (text: string, percentage: number) => { + this.options.onProgress(text, percentage); + }; + + cancel = () => { + this.isCanceled = true; + this.options.onCancel(); + + for (const request of this.requests) { + request.cancel(); + } + }; +} + +export type RecordServiceState = Record> = { + persistedState?: PersistedState; +}; + +export interface RecordServiceContextOptions extends ServiceContextOptions { + apertureOptions: ApertureOptions; + state: State; + setRecordingName: (name: string) => void; +} + +export class RecordServiceContext extends ServiceContext { + private readonly options: RecordServiceContextOptions; + + constructor(options: RecordServiceContextOptions) { + super(options); + this.options = options; + } + + get state() { + return this.options.state; + } + + get apertureOptions() { + return this.options.apertureOptions; + } + + get setRecordingName() { + return this.options.setRecordingName; + } +} diff --git a/main/plugins/service.ts b/main/plugins/service.ts new file mode 100644 index 000000000..a270dcba3 --- /dev/null +++ b/main/plugins/service.ts @@ -0,0 +1,29 @@ + +import PCancelable from 'p-cancelable'; +import {Format} from '../common/types'; +import {Schema} from '../utils/ajv'; +import {EditServiceContext, RecordServiceContext, ShareServiceContext} from './service-context'; + +export interface Service { + title: string; + configDescription?: string; + config?: {[P in keyof Config]: Schema}; +} + +export interface ShareService extends Service { + formats: Format[]; + action: (context: ShareServiceContext) => PromiseLike | PCancelable; +} + +export interface EditService extends Service { + action: (context: EditServiceContext) => PromiseLike | PCancelable; +} + +export type RecordServiceHook = 'willStartRecording' | 'didStartRecording' | 'didStopRecording'; + +export type RecordService = Service & { + [key in RecordServiceHook]: ((context: RecordServiceContext) => PromiseLike) | undefined; +} & { + willEnable?: () => PromiseLike; + cleanUp?: (persistedState: Record) => void; +}; diff --git a/main/recording-history.js b/main/recording-history.ts similarity index 71% rename from main/recording-history.js rename to main/recording-history.ts index 0ea249395..d534106da 100644 --- a/main/recording-history.js +++ b/main/recording-history.ts @@ -1,22 +1,39 @@ /* eslint-disable array-element-newline */ 'use strict'; -const {shell, clipboard} = require('electron'); -const fs = require('fs'); -const Store = require('electron-store'); -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const util = require('electron-util'); -const execa = require('execa'); -const tempy = require('tempy'); - -const {showDialog} = require('./dialog'); -const {openEditorWindow} = require('./editor'); -const plugins = require('./common/plugins'); -const {generateTimestampedName} = require('./utils/timestamped-name'); +import {shell, clipboard} from 'electron'; +import fs from 'fs'; +import Store from 'electron-store'; +import util from 'electron-util'; +import execa from 'execa'; +import tempy from 'tempy'; +import {SetOptional} from 'type-fest'; + +import {windowManager} from './windows/manager'; +import {plugins} from './plugins'; +import {generateTimestampedName} from './utils/timestamped-name'; +import {Video} from './video'; +import {ApertureOptions} from './common/types'; +import Sentry, {isSentryEnabled} from './utils/sentry'; +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); -const recordingHistory = new Store({ +export interface PastRecording { + filePath: string; + name: string; + date: string; +} + +export interface ActiveRecording extends PastRecording { + apertureOptions: ApertureOptions; + plugins: Record>; +} + +export const recordingHistory = new Store<{ + activeRecording: ActiveRecording; + recordings: PastRecording[]; +}>({ name: 'recording-history', schema: { activeRecording: { @@ -60,13 +77,13 @@ const recordingHistory = new Store({ } }); -const setCurrentRecording = ({ +export const setCurrentRecording = ({ filePath, name = generateTimestampedName(), date = new Date().toISOString(), apertureOptions, plugins = {} -}) => { +}: SetOptional) => { recordingHistory.set('activeRecording', { filePath, name, @@ -76,35 +93,35 @@ const setCurrentRecording = ({ }); }; -const updatePluginState = state => { +export const updatePluginState = (state: ActiveRecording['plugins']) => { recordingHistory.set('activeRecording.plugins', state); }; -const stopCurrentRecording = recordingName => { +export const stopCurrentRecording = (recordingName?: string) => { const {filePath, name} = recordingHistory.get('activeRecording'); addRecording({ filePath, - name: recordingName || name, + name: recordingName ?? name, date: new Date().toISOString() }); recordingHistory.delete('activeRecording'); }; -const getPastRecordings = () => { +export const getPastRecordings = (): PastRecording[] => { const recordings = recordingHistory.get('recordings', []); const validRecordings = recordings.filter(({filePath}) => fs.existsSync(filePath)); recordingHistory.set('recordings', validRecordings); return validRecordings; }; -const addRecording = newRecording => { +export const addRecording = (newRecording: PastRecording): PastRecording[] => { const recordings = [newRecording, ...recordingHistory.get('recordings', [])]; const validRecordings = recordings.filter(({filePath}) => fs.existsSync(filePath)); recordingHistory.set('recordings', validRecordings); return validRecordings; }; -const cleanPastRecordings = () => { +export const cleanPastRecordings = () => { const recordings = getPastRecordings(); for (const recording of recordings) { fs.unlinkSync(recording.filePath); @@ -113,22 +130,22 @@ const cleanPastRecordings = () => { recordingHistory.set('recordings', []); }; -const cleanUpRecordingPlugins = usedPlugins => { - const recordingPlugins = plugins.getRecordingPlugins(); +export const cleanUpRecordingPlugins = (usedPlugins: ActiveRecording['plugins']) => { + const recordingPlugins = plugins.recordingPlugins; for (const pluginName of Object.keys(usedPlugins)) { const plugin = recordingPlugins.find(p => p.name === pluginName); for (const [serviceTitle, persistedState] of Object.entries(usedPlugins[pluginName])) { - const service = plugin && plugin.recordServices.find(s => s.title === serviceTitle); + const service = plugin?.recordServices.find(s => s.title === serviceTitle); - if (service && service.cleanUp) { + if (service?.cleanUp) { service.cleanUp(persistedState); } } } }; -const handleIncompleteRecording = async recording => { +export const handleIncompleteRecording = async (recording: ActiveRecording) => { cleanUpRecordingPlugins(recording.plugins); try { @@ -147,14 +164,14 @@ const handleIncompleteRecording = async recording => { return handleRecording(recording); }; -const handleRecording = async recording => { +const handleRecording = async (recording: ActiveRecording) => { addRecording({ filePath: recording.filePath, name: recording.name, date: recording.date }); - return showDialog({ + return windowManager.dialog?.open({ title: 'Kap didn\'t shut down correctly.', detail: 'Looks like Kap crashed during a recording. Kap was able to locate the file and it appears to be playable.', buttons: [ @@ -167,15 +184,15 @@ const handleRecording = async recording => { }, { label: 'Show in Editor', - action: () => openEditorWindow(recording.filePath, {recordingName: recording.name}) + action: async () => Video.getOrCreate({filePath: recording.filePath, title: recording.name}).openEditorWindow() } ] }); }; const knownErrors = [{ - test: error => error.includes('moov atom not found'), - fix: async filePath => { + test: (error: string) => error.includes('moov atom not found'), + fix: async (filePath: string): Promise => { try { const outputPath = tempy.file({extension: 'mp4'}); @@ -194,12 +211,12 @@ const knownErrors = [{ ]); return outputPath; - } catch { } + } catch {} } }]; -const handleCorruptRecording = async (recording, error) => { - const options = { +const handleCorruptRecording = async (recording: ActiveRecording, error: string) => { + const options: any = { title: 'Kap didn\'t shut down correctly.', detail: `Looks like Kap crashed during a recording. We were able to locate the file. Unfortunately, it appears to be corrupt.\n\n${error}`, cancelId: 0, @@ -224,13 +241,12 @@ const handleCorruptRecording = async (recording, error) => { const applicableErrors = knownErrors.filter(({test}) => test(error)); if (applicableErrors.length === 0) { - const Sentry = require('./utils/sentry'); - if (Sentry.isSentryEnabled) { + if (isSentryEnabled) { // Collect info about possible unknown errors, to see if we can implement fixes using ffmpeg Sentry.captureException(new Error(`Corrupt recording: ${error}`)); } - return showDialog(options); + return windowManager.dialog?.open(options); } options.message = 'We can attempt to repair the recording.'; @@ -238,9 +254,8 @@ const handleCorruptRecording = async (recording, error) => { options.buttons.push({ label: 'Attempt to Fix', activeLabel: 'Attempting to Fix…', - action: async (_, updateUi) => { + action: async (_: any, updateUi: any) => { for (const {fix} of applicableErrors) { - // eslint-disable-next-line no-await-in-loop const outputPath = await fix(recording.filePath); if (outputPath) { @@ -263,7 +278,7 @@ const handleCorruptRecording = async (recording, error) => { }, { label: 'Show in Editor', - action: () => openEditorWindow(outputPath, {recordingName: recording.name}) + action: async () => Video.getOrCreate({filePath: outputPath, title: recording.name}).openEditorWindow() } ] }); @@ -292,10 +307,10 @@ const handleCorruptRecording = async (recording, error) => { } }); - return showDialog(options); + return windowManager.dialog?.open(options); }; -const hasActiveRecording = async () => { +export const hasActiveRecording = async () => { const activeRecording = recordingHistory.get('activeRecording'); if (activeRecording) { @@ -306,14 +321,3 @@ const hasActiveRecording = async () => { return false; }; - -module.exports = { - recordingHistory, - getPastRecordings, - addRecording, - hasActiveRecording, - setCurrentRecording, - updatePluginState, - stopCurrentRecording, - cleanPastRecordings -}; diff --git a/main/remote-states/conversion.ts b/main/remote-states/conversion.ts new file mode 100644 index 000000000..fbd5e8bcf --- /dev/null +++ b/main/remote-states/conversion.ts @@ -0,0 +1,61 @@ +import {ConversionRemoteState, ConversionState} from '../common/types'; +import Conversion from '../conversion'; + +const getConversionState = (conversion: Conversion): ConversionState => { + return { + title: conversion.title, + description: conversion.description, + message: conversion.text, + progress: conversion.percentage, + size: conversion.finalSize, + status: conversion.status, + canCopy: conversion.canCopy + }; +}; + +const conversionRemoteState: ConversionRemoteState = (sendUpdate: (state: ConversionState, id: string) => void) => { + const getState = (conversionId: string) => { + const conversion = Conversion.fromId(conversionId); + + if (!conversion) { + return; + } + + return getConversionState(conversion); + }; + + const subscribe = (conversionId: string) => { + const conversion = Conversion.fromId(conversionId); + + if (!conversion) { + return; + } + + const callback = () => { + sendUpdate(getConversionState(conversion), conversionId); + }; + + conversion.on('updated', callback); + return () => { + conversion.off('updated', callback); + }; + }; + + const actions = { + cancel: (_: any, conversionId: string) => { + Conversion.fromId(conversionId)?.cancel(); + }, + copy: (_: any, conversionId: string) => { + Conversion.fromId(conversionId)?.copy(); + } + } as any; + + return { + subscribe, + getState, + actions + }; +}; + +export default conversionRemoteState; +export const name = 'conversion'; diff --git a/main/remote-states/editor-options.ts b/main/remote-states/editor-options.ts new file mode 100644 index 000000000..d20b84f03 --- /dev/null +++ b/main/remote-states/editor-options.ts @@ -0,0 +1,140 @@ +import Store from 'electron-store'; +import {EditorOptionsRemoteState, ExportOptions, ExportOptionsPlugin, Format} from '../common/types'; +import {formats} from '../common/constants'; + +import {plugins} from '../plugins'; +import {apps} from '../plugins/built-in/open-with-plugin'; +import {prettifyFormat} from '../utils/formats'; + +const exportUsageHistory = new Store<{[key in Format]: {lastUsed: number; plugins: Record}}>({ + name: 'export-usage-history', + defaults: { + gif: {lastUsed: 5, plugins: {default: 1}}, + mp4: {lastUsed: 4, plugins: {default: 1}}, + webm: {lastUsed: 3, plugins: {default: 1}}, + av1: {lastUsed: 2, plugins: {default: 1}}, + apng: {lastUsed: 1, plugins: {default: 1}} + } +}); + +const fpsUsageHistory = new Store<{[key in Format]: number}>({ + name: 'fps-usage-history', + schema: { + apng: { + type: 'number', + minimum: 0, + default: 60 + }, + webm: { + type: 'number', + minimum: 0, + default: 60 + }, + mp4: { + type: 'number', + minimum: 0, + default: 60 + }, + gif: { + type: 'number', + minimum: 0, + default: 60 + }, + av1: { + type: 'number', + minimum: 0, + default: 60 + } + } +}); + +const getEditOptions = () => { + return plugins.editPlugins.flatMap( + plugin => plugin.editServices + .filter(service => plugin.config.validServices.includes(service.title)) + .map(service => ({ + title: service.title, + pluginName: plugin.name, + pluginPath: plugin.pluginPath, + hasConfig: Object.keys(service.config ?? {}).length > 0 + })) + ); +}; + +const getExportOptions = () => { + const installed = plugins.sharePlugins; + + const options = formats.map(format => ({ + format, + prettyFormat: prettifyFormat(format), + plugins: [] as ExportOptionsPlugin[], + lastUsed: exportUsageHistory.get(format).lastUsed + })); + + const sortFunc = (a: T, b: T) => b.lastUsed - a.lastUsed; + + for (const plugin of installed) { + if (!plugin.isCompatible) { + continue; + } + + for (const service of plugin.shareServices) { + for (const format of service.formats) { + options.find(option => option.format === format)?.plugins.push({ + title: service.title, + pluginName: plugin.name, + pluginPath: plugin.pluginPath, + apps: plugin.name === '_openWith' ? apps.get(format) : undefined, + lastUsed: exportUsageHistory.get(format).plugins?.[plugin.name] ?? 0 + }); + } + } + } + + return options.map(option => ({...option, plugins: option.plugins.sort(sortFunc)})).sort(sortFunc); +}; + +const editorOptionsRemoteState: EditorOptionsRemoteState = (sendUpdate: (state: ExportOptions) => void) => { + const state: ExportOptions = { + formats: getExportOptions(), + editServices: getEditOptions(), + fpsHistory: fpsUsageHistory.store + }; + + const updatePlugins = () => { + state.formats = getExportOptions(); + state.editServices = getEditOptions(); + sendUpdate(state); + }; + + plugins.on('installed', updatePlugins); + plugins.on('uninstalled', updatePlugins); + plugins.on('config-changed', updatePlugins); + + const actions = { + updatePluginUsage: ({format, plugin}: {format: Format; plugin: string}) => { + const usage = exportUsageHistory.get(format); + const now = Date.now(); + + usage.plugins[plugin] = now; + usage.lastUsed = now; + exportUsageHistory.set(format, usage); + + state.formats = getExportOptions(); + sendUpdate(state); + }, + updateFpsUsage: ({format, fps}: {format: Format; fps: number}) => { + fpsUsageHistory.set(format, fps); + state.fpsHistory = fpsUsageHistory.store; + sendUpdate(state); + } + }; + + return { + actions, + getState: () => state + }; +}; + +export default editorOptionsRemoteState; +export const name = 'editor-options'; diff --git a/main/remote-states/index.ts b/main/remote-states/index.ts new file mode 100644 index 000000000..b9fe20eea --- /dev/null +++ b/main/remote-states/index.ts @@ -0,0 +1,10 @@ +import setupRemoteState from './setup-remote-state'; + +const remoteStateNames = ['editor-options', 'conversion']; + +export const setupRemoteStates = async () => { + return Promise.all(remoteStateNames.map(async fileName => { + const state = require(`./${fileName}`); + setupRemoteState(state.name, state.default); + })); +}; diff --git a/main/remote-states/setup-remote-state.ts b/main/remote-states/setup-remote-state.ts new file mode 100644 index 000000000..438a3c109 --- /dev/null +++ b/main/remote-states/setup-remote-state.ts @@ -0,0 +1,55 @@ +import {RemoteState, getChannelNames} from './utils'; +import {ipcMain} from 'electron-better-ipc'; +import {BrowserWindow} from 'electron'; + +// eslint-disable-next-line @typescript-eslint/ban-types +const setupRemoteState = async >(name: string, callback: RemoteState) => { + const channelNames = getChannelNames(name); + + const renderers = new Map(); + + const sendUpdate = async (state?: State, id?: string) => { + if (id) { + const renderer = renderers.get(id); + + if (renderer) { + ipcMain.callRenderer(renderer, channelNames.stateUpdated, state); + } + + return; + } + + for (const [windowId, renderer] of renderers.entries()) { + if (renderer && !renderer.isDestroyed()) { + ipcMain.callRenderer(renderer, channelNames.stateUpdated, state ?? (await getState?.(windowId))); + } + } + }; + + const {getState, actions = {}, subscribe} = await callback(sendUpdate); + + ipcMain.answerRenderer(channelNames.subscribe, (customId: string, window: BrowserWindow) => { + const id = customId ?? window.id.toString(); + renderers.set(id, window); + const unsubscribe = subscribe?.(id); + + window.on('close', () => { + renderers.delete(id); + unsubscribe?.(); + }); + + return Object.keys(actions); + }); + + ipcMain.answerRenderer(channelNames.getState, async (customId: string, window: BrowserWindow) => { + const id = customId ?? window.id.toString(); + return getState(id); + }); + + ipcMain.answerRenderer(channelNames.callAction, ({key, data, id: customId}: any, window: BrowserWindow) => { + const id = customId || window.id.toString(); + return (actions as any)[key]?.(data, id); + }); +}; + +export default setupRemoteState; diff --git a/main/remote-states/utils.ts b/main/remote-states/utils.ts new file mode 100644 index 000000000..55402bc0d --- /dev/null +++ b/main/remote-states/utils.ts @@ -0,0 +1,17 @@ +import {Promisable} from 'type-fest'; + +export const getChannelName = (name: string, action: string) => `kap-remote-state-${name}-${action}`; + +export const getChannelNames = (name: string) => ({ + subscribe: getChannelName(name, 'subscribe'), + getState: getChannelName(name, 'get-state'), + callAction: getChannelName(name, 'call-action'), + stateUpdated: getChannelName(name, 'state-updated') +}); + +// eslint-disable-next-line @typescript-eslint/ban-types +export type RemoteState> = (sendUpdate: (state?: State, id?: string) => void) => Promisable<{ + getState: (id?: string) => Promisable; + actions: Actions; + subscribe?: (id?: string) => undefined | (() => void); +}>; diff --git a/main/service-context.js b/main/service-context.js deleted file mode 100644 index 808f264b8..000000000 --- a/main/service-context.js +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; -const {Notification, clipboard} = require('electron'); -const got = require('got'); -const {addPluginPromise} = require('./utils/deep-linking'); - -const prettifyFormat = format => { - const formats = new Map([ - ['apng', 'APNG'], - ['gif', 'GIF'], - ['mp4', 'MP4'], - ['webm', 'WebM'] - ]); - - return formats.get(format); -}; - -class ServiceContext { - constructor(options) { - this._isBuiltin = options._isBuiltin; - this.config = options.config; - - this.requests = []; - this.isCanceled = false; - - this.request = this.request.bind(this); - this.copyToClipboard = this.copyToClipboard.bind(this); - this.notify = this.notify.bind(this); - this.openConfigFile = this.openConfigFile.bind(this); - this.waitForDeepLink = this.waitForDeepLink.bind(this); - } - - request(url, options) { - if (this.isCanceled) { - return; - } - - const request = got(url, options); - - this.requests.push(request); - - return request; - } - - cancel() { - this.isCanceled = true; - if (this.onCancel) { - this.onCancel(); - } - - for (const request of this.requests) { - request.cancel(); - } - } - - copyToClipboard(text) { - if (this.isCanceled) { - return; - } - - clipboard.writeText(text); - } - - notify(text, action) { - if (this.isCanceled) { - return; - } - - let options = { - title: this.pluginName, - body: text - }; - - if (this._isBuiltin) { - options = { - body: text - }; - } - - const notification = new Notification(options); - - if (action) { - notification.on('click', action); - } - - notification.show(); - } - - openConfigFile() { - if (this.isCanceled) { - return; - } - - this.config.openInEditor(); - } - - async waitForDeepLink() { - return new Promise(resolve => { - addPluginPromise(this.pluginName, resolve); - }); - } -} - -class ShareServiceContext extends ServiceContext { - constructor(options) { - super(options); - - this.format = options.format; - this.prettyFormat = prettifyFormat(this.format); - this.defaultFileName = options.defaultFileName; - this.filePath = options.convert; - this.onCancel = options.onCancel; - this.onProgress = options.onProgress; - this.pluginName = options.pluginName; - - this.isCanceled = false; - - this.cancel = this.cancel.bind(this); - this.setProgress = this.setProgress.bind(this); - } - - clear() { - this.isCanceled = true; - - for (const request of this.requests) { - request.cancel(); - } - } - - setProgress(text, percentage) { - if (this.isCanceled) { - return; - } - - this.onProgress(text, percentage); - } -} - -class RecordServiceContext extends ServiceContext { - constructor(options) { - super(options); - - this.apertureOptions = options.apertureOptions; - this.state = options.state; - this.setRecordingName = options.setRecordingName; - } -} - -class EditServiceContext extends ServiceContext { - constructor(options) { - super(options); - - const { - inputPath, - outputPath, - width, - height, - format, - fps, - startTime, - endTime, - isMuted, - loop - } = options.exportOptions; - - this.inputPath = inputPath; - this.outputPath = outputPath; - - this.exportOptions = { - width, - height, - format, - fps, - duration: endTime - startTime, - isMuted, - loop - }; - this.convert = options.convert; - } -} - -module.exports = { - ShareServiceContext, - RecordServiceContext, - EditServiceContext -}; diff --git a/main/tray.js b/main/tray.ts similarity index 58% rename from main/tray.js rename to main/tray.ts index 1949144a7..a4b46a3ad 100644 --- a/main/tray.js +++ b/main/tray.ts @@ -1,21 +1,22 @@ 'use strict'; -const {Tray} = require('electron'); -const path = require('path'); +import {Tray} from 'electron'; +import path from 'path'; +import {getCogMenu} from './menus/cog'; +import {track} from './common/analytics'; +import {openFiles} from './utils/open-files'; +import {windowManager} from './windows/manager'; -const {openCropperWindow} = require('./cropper'); -const {getCogMenu} = require('./menus'); -const {track} = require('./common/analytics'); -const openFiles = require('./utils/open-files'); - -let tray = null; -let trayAnimation = null; +let tray: Tray; +let trayAnimation: NodeJS.Timeout | undefined; const openContextMenu = async () => { tray.popUpContextMenu(await getCogMenu()); }; -const initializeTray = () => { +const openCropperWindow = () => windowManager.cropper?.open(); + +export const initializeTray = () => { tray = new Tray(path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png')); tray.on('click', openCropperWindow); tray.on('right-click', openContextMenu); @@ -27,28 +28,33 @@ const initializeTray = () => { return tray; }; -const disableTray = () => { +export const disableTray = () => { tray.removeListener('click', openCropperWindow); tray.removeListener('right-click', openContextMenu); }; -const resetTray = () => { +export const resetTray = () => { if (trayAnimation) { clearTimeout(trayAnimation); } + // TODO: figure out why it's marked like this. Tray extends EventEmitter, so removeAllListeners should be available + // @ts-expect-error tray.removeAllListeners('click'); + tray.setImage(path.join(__dirname, '..', 'static', 'menubarDefaultTemplate.png')); tray.on('click', openCropperWindow); tray.on('right-click', openContextMenu); }; -const setRecordingTray = stopRecording => { +export const setRecordingTray = (stopRecording: () => void) => { animateIcon(); + + // TODO: figure out why this is marked as missing. It's defined properly in the electron.d.ts file tray.once('click', stopRecording); }; -const animateIcon = () => new Promise(resolve => { +const animateIcon = async () => new Promise(resolve => { const interval = 20; let i = 0; @@ -60,8 +66,8 @@ const animateIcon = () => new Promise(resolve => { try { tray.setImage(path.join(__dirname, '..', 'static', 'menubar-loading', filename)); next(); - } catch (_) { - trayAnimation = null; + } catch { + trayAnimation = undefined; resolve(); } }, interval); @@ -69,10 +75,3 @@ const animateIcon = () => new Promise(resolve => { next(); }); - -module.exports = { - initializeTray, - disableTray, - setRecordingTray, - resetTray -}; diff --git a/main/utils/ajv.js b/main/utils/ajv.js deleted file mode 100644 index 937f5e991..000000000 --- a/main/utils/ajv.js +++ /dev/null @@ -1,37 +0,0 @@ -const Ajv = require('ajv'); - -const hexColorValidator = () => { - return { - type: 'string', - pattern: /^((0x)|#)([\dA-Fa-f]{8}|[\dA-Fa-f]{6})$/.source - }; -}; - -const keyboardShortcutValidator = () => { - return { - type: 'string' - }; -}; - -const validators = new Map([ - ['hexColor', hexColorValidator], - ['keyboardShortcut', keyboardShortcutValidator] -]); - -class CustomAjv extends Ajv { - constructor(options) { - super(options); - - this.addKeyword('customType', { - macro: (schema, parentSchema) => { - return validators.get(schema)(parentSchema); - }, - metaSchema: { - type: 'string', - enum: [...validators.keys()] - } - }); - } -} - -module.exports = CustomAjv; diff --git a/main/utils/ajv.ts b/main/utils/ajv.ts new file mode 100644 index 000000000..23f50d4d3 --- /dev/null +++ b/main/utils/ajv.ts @@ -0,0 +1,50 @@ +import Ajv, {Options} from 'ajv'; +import {Schema as JSONSchema} from 'electron-store'; +import {Except} from 'type-fest'; + +export type Schema = Except, 'required'> & { + required?: boolean; + customType?: string; +}; + +const hexColorValidator = () => { + return { + type: 'string', + pattern: /^((0x)|#)([\dA-Fa-f]{8}|[\dA-Fa-f]{6})$/.source + }; +}; + +const keyboardShortcutValidator = () => { + return { + type: 'string' + }; +}; + +// eslint-disable-next-line @typescript-eslint/ban-types +const validators = new Map object>([ + ['hexColor', hexColorValidator], + ['keyboardShortcut', keyboardShortcutValidator] +]); + +export default class CustomAjv extends Ajv { + constructor(options: Options) { + super(options); + + this.addKeyword('customType', { + // eslint-disable-next-line @typescript-eslint/ban-types + macro: (schema: string, parentSchema: object) => { + const validator = validators.get(schema); + + if (!validator) { + throw new Error(`No custom type found for ${schema}`); + } + + return validator(parentSchema); + }, + metaSchema: { + type: 'string', + enum: [...validators.keys()] + } + }); + } +} diff --git a/main/utils/deep-linking.js b/main/utils/deep-linking.ts similarity index 53% rename from main/utils/deep-linking.js rename to main/utils/deep-linking.ts index b02f3c33d..13174957f 100644 --- a/main/utils/deep-linking.js +++ b/main/utils/deep-linking.ts @@ -1,13 +1,12 @@ -'use strict'; +import {windowManager} from '../windows/manager'; -const {openPrefsWindow} = require('../preferences'); -const pluginPromises = new Map(); +const pluginPromises = new Map void>(); -const handlePluginsDeepLink = path => { +const handlePluginsDeepLink = (path: string) => { const [plugin, ...rest] = path.split('/'); if (pluginPromises.has(plugin)) { - pluginPromises.get(plugin)(rest.join('/')); + pluginPromises.get(plugin)?.(rest.join('/')); pluginPromises.delete(plugin); return; } @@ -15,11 +14,11 @@ const handlePluginsDeepLink = path => { console.error(`Received link for plugin “${plugin}” but there was no registered listener.`); }; -const addPluginPromise = (plugin, resolveFunction) => { +export const addPluginPromise = (plugin: string, resolveFunction: (path: string) => void) => { pluginPromises.set(plugin, resolveFunction); }; -const triggerPluginAction = action => name => openPrefsWindow({target: {name, action}}); +const triggerPluginAction = (action: string) => (name: string) => windowManager.preferences?.open({target: {name, action}}); const routes = new Map([ ['plugins', handlePluginsDeepLink], @@ -27,17 +26,12 @@ const routes = new Map([ ['configure-plugin', triggerPluginAction('configure')] ]); -const handleDeepLink = url => { +export const handleDeepLink = (url: string) => { const {host, pathname} = new URL(url); if (routes.has(host)) { - return routes.get(host)(pathname.slice(1)); + return routes.get(host)?.(pathname.slice(1)); } console.error(`Route not recognized: ${host} (${url}).`); }; - -module.exports = { - handleDeepLink, - addPluginPromise -}; diff --git a/main/utils/devices.js b/main/utils/devices.ts similarity index 53% rename from main/utils/devices.js rename to main/utils/devices.ts index e7d14f6b9..667ced430 100644 --- a/main/utils/devices.js +++ b/main/utils/devices.ts @@ -1,12 +1,13 @@ -'use strict'; - -const audioDevices = require('macos-audio-devices'); +import {hasMicrophoneAccess} from '../common/system-permissions'; +import * as audioDevices from 'macos-audio-devices'; +import {settings} from '../common/settings'; +import {defaultInputDeviceId} from '../common/constants'; +import Sentry from './sentry'; const aperture = require('aperture'); const {showError} = require('./errors'); -const {hasMicrophoneAccess} = require('../common/system-permissions'); -const getAudioDevices = async () => { +export const getAudioDevices = async () => { if (!hasMicrophoneAccess()) { return []; } @@ -34,7 +35,6 @@ const getAudioDevices = async () => { const devices = await aperture.audioDevices(); if (!Array.isArray(devices)) { - const Sentry = require('./sentry'); Sentry.captureException(new Error(`devices is not an array: ${JSON.stringify(devices)}`)); showError(error); return []; @@ -48,7 +48,7 @@ const getAudioDevices = async () => { } }; -const getDefaultInputDevice = () => { +export const getDefaultInputDevice = () => { try { const device = audioDevices.getDefaultInputDevice.sync(); return { @@ -57,7 +57,29 @@ const getDefaultInputDevice = () => { }; } catch { // Running on 10.13 and don't have swift support libs. No need to report + return undefined; + } +}; + +export const getSelectedInputDeviceId = () => { + const audioInputDeviceId = settings.get('audioInputDeviceId', defaultInputDeviceId); + + if (audioInputDeviceId === defaultInputDeviceId) { + const device = getDefaultInputDevice(); + return device?.id; } + + return audioInputDeviceId; }; -module.exports = {getAudioDevices, getDefaultInputDevice}; +export const initializeDevices = async () => { + const audioInputDeviceId = settings.get('audioInputDeviceId'); + + if (hasMicrophoneAccess()) { + const devices = await getAudioDevices(); + + if (!devices.some((device: any) => device.id === audioInputDeviceId)) { + settings.set('audioInputDeviceId', defaultInputDeviceId); + } + } +}; diff --git a/main/utils/dock.js b/main/utils/dock.ts similarity index 61% rename from main/utils/dock.js rename to main/utils/dock.ts index c2dbf132a..fbf10d546 100644 --- a/main/utils/dock.js +++ b/main/utils/dock.ts @@ -1,6 +1,7 @@ -const {app} = require('electron'); +import {app} from 'electron'; +import {Promisable} from 'type-fest'; -const ensureDockIsShowing = async action => { +export const ensureDockIsShowing = async (action: () => Promisable) => { const wasDockShowing = app.dock.isVisible(); if (!wasDockShowing) { await app.dock.show(); @@ -13,7 +14,7 @@ const ensureDockIsShowing = async action => { } }; -const ensureDockIsShowingSync = action => { +export const ensureDockIsShowingSync = (action: () => void) => { const wasDockShowing = app.dock.isVisible(); if (!wasDockShowing) { app.dock.show(); @@ -25,8 +26,3 @@ const ensureDockIsShowingSync = action => { app.dock.hide(); } }; - -module.exports = { - ensureDockIsShowing, - ensureDockIsShowingSync -}; diff --git a/main/utils/encoding.js b/main/utils/encoding.ts similarity index 54% rename from main/utils/encoding.js rename to main/utils/encoding.ts index 000cbb0ab..5be357582 100644 --- a/main/utils/encoding.js +++ b/main/utils/encoding.ts @@ -1,26 +1,27 @@ /* eslint-disable array-element-newline */ -'use strict'; -const path = require('path'); -const tmp = require('tmp'); -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const util = require('electron-util'); -const execa = require('execa'); -const {track} = require('../common/analytics'); +import path from 'path'; +import util from 'electron-util'; +import execa from 'execa'; +import tempy from 'tempy'; +import {track} from '../common/analytics'; +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); -const getEncoding = async filePath => { +export const getEncoding = async (filePath: string) => { try { await execa(ffmpegPath, ['-i', filePath]); + return undefined; } catch (error) { - return /.*: Video: (.*?) \(.*/.exec(error.stderr)[1]; + return /.*: Video: (.*?) \(.*/.exec(error.stderr)?.[1]; } }; // `ffmpeg -i original.mp4 -vcodec libx264 -crf 27 -preset veryfast -c:a copy output.mp4` -const convertToH264 = async inputPath => { - const outputPath = tmp.tmpNameSync({postfix: path.extname(inputPath)}); +export const convertToH264 = async (inputPath: string) => { + const outputPath = tempy.file({extension: path.extname(inputPath)}); + track('encoding/converted/hevc'); await execa(ffmpegPath, [ @@ -34,8 +35,3 @@ const convertToH264 = async inputPath => { return outputPath; }; - -module.exports = { - getEncoding, - convertToH264 -}; diff --git a/main/utils/errors.js b/main/utils/errors.ts similarity index 59% rename from main/utils/errors.js rename to main/utils/errors.ts index a63cc6ca9..04d8c47b0 100644 --- a/main/utils/errors.js +++ b/main/utils/errors.ts @@ -1,16 +1,16 @@ -'use strict'; - -const path = require('path'); -const {clipboard, shell, app} = require('electron'); -const ensureError = require('ensure-error'); -const cleanStack = require('clean-stack'); -const isOnline = require('is-online'); -const {openNewGitHubIssue} = require('electron-util'); -const got = require('got'); -const delay = require('delay'); -const macosRelease = require('macos-release'); - -const {showDialog} = require('../dialog'); +import path from 'path'; +import {clipboard, shell, app} from 'electron'; +import ensureError from 'ensure-error'; +import cleanStack from 'clean-stack'; +import isOnline from 'is-online'; +import {openNewGitHubIssue} from 'electron-util'; +import got from 'got'; +import delay from 'delay'; +import macosRelease from 'macos-release'; + +import {windowManager} from '../windows/manager'; +import Sentry, {isSentryEnabled} from './sentry'; +import {InstalledPlugin} from '../plugins/plugin'; const MAX_RETRIES = 10; @@ -20,9 +20,23 @@ const ERRORS_TO_IGNORE = [ /net::ERR_CONNECTION_CLOSED/ ]; -const shouldIgnoreError = errorText => ERRORS_TO_IGNORE.some(regex => regex.test(errorText)); +const shouldIgnoreError = (errorText: string) => ERRORS_TO_IGNORE.some(regex => regex.test(errorText)); + +type SentryIssue = { + issueId: string; + shortId: string; + permalink: string; + ghUrl: string; +} | { + issueId: string; + shortId: string; + permalink: string; + ghIssueTemplate: string; +} | { + error: string; +}; -const getSentryIssue = async (eventId, tries = 0) => { +const getSentryIssue = async (eventId: string, tries = 0): Promise => { if (tries > MAX_RETRIES) { return; } @@ -36,7 +50,7 @@ const getSentryIssue = async (eventId, tries = 0) => { if (body.pending) { await delay(2000); - return getSentryIssue(eventId, tries + 1); + return await getSentryIssue(eventId, tries + 1); } return body; @@ -46,14 +60,14 @@ const getSentryIssue = async (eventId, tries = 0) => { } }; -const getPrettyStack = error => { +const getPrettyStack = (error: Error) => { const pluginsPath = path.join(app.getPath('userData'), 'plugins', 'node_modules'); - return cleanStack(error.stack, {pretty: true, basePath: pluginsPath}); + return cleanStack(error.stack ?? '', {pretty: true, basePath: pluginsPath}); }; const release = macosRelease(); -const getIssueBody = (title, errorStack, sentryTemplate) => ` +const getIssueBody = (title: string, errorStack: string, sentryTemplate = '') => ` ${sentryTemplate} @@ -70,11 +84,22 @@ ${errorStack} `; -const showError = async (error, {title: customTitle, plugin} = {}) => { +export const showError = async ( + error: Error, + { + title: customTitle, + plugin + }: { + title?: string; + plugin?: InstalledPlugin; + } = {} +) => { + await app.whenReady(); const ensuredError = ensureError(error); - const title = customTitle || ensuredError.name; + const title = customTitle ?? ensuredError.name; const detail = getPrettyStack(ensuredError); + console.log(error); if (shouldIgnoreError(`${title}\n${detail}`)) { return; } @@ -83,7 +108,9 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { 'Don\'t Report', { label: 'Copy Error', - action: () => clipboard.writeText(`${title}\n${detail}`) + action: () => { + clipboard.writeText(`${title}\n${detail}`); + } } ]; @@ -100,7 +127,7 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { } }; - return showDialog({ + return windowManager.dialog?.open({ title, detail, cancelId: 0, @@ -109,13 +136,10 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { }); } - // Avoids circular dependency - const Sentry = require('./sentry'); - let message; - const buttons = [...mainButtons]; + const buttons: any[] = [...mainButtons]; - if (isOnline && Sentry.isSentryEnabled) { + if (isSentryEnabled && await isOnline()) { const eventId = Sentry.captureException(ensuredError); const sentryIssuePromise = getSentryIssue(eventId); @@ -124,21 +148,21 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { buttons.push({ label: 'Collect Info and Report', activeLabel: 'Collecting Info…', - action: async (_, updateUi) => { + action: async (_: unknown, updateUi: any) => { const issue = await sentryIssuePromise; - if (!issue || issue.error) { + if (!issue || 'error' in issue) { updateUi({ message: 'Something went wrong while collecting the information.', buttons: mainButtons }); - } else if (issue.ghUrl) { + } else if ('ghUrl' in issue) { updateUi({ message: 'This issue is already being tracked!', buttons: [ ...mainButtons, { label: 'View Issue', - action: () => shell.openExternal(issue.ghUrl) + action: async () => shell.openExternal(issue.ghUrl) } ] }); @@ -148,13 +172,15 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { ...mainButtons, { label: 'Open Issue', - action: () => openNewGitHubIssue({ - user: 'wulkano', - repo: 'kap', - title, - body: getIssueBody(title, detail, issue.ghIssueTemplate), - labels: ['sentry'] - }) + action: () => { + openNewGitHubIssue({ + user: 'wulkano', + repo: 'kap', + title, + body: getIssueBody(title, detail, issue.ghIssueTemplate), + labels: ['sentry'] + }); + } } ] }); @@ -163,7 +189,7 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { }); } - return showDialog({ + return windowManager.dialog?.open({ title, detail, buttons, @@ -173,14 +199,12 @@ const showError = async (error, {title: customTitle, plugin} = {}) => { }); }; -const setupErrorHandling = () => { +export const setupErrorHandling = () => { process.on('uncaughtException', error => { showError(error, {title: 'Unhandled Error'}); }); process.on('unhandledRejection', error => { - showError(error, {title: 'Unhandled Promise Rejection'}); + showError(ensureError(error), {title: 'Unhandled Promise Rejection'}); }); }; - -module.exports = {showError, setupErrorHandling}; diff --git a/main/utils/formats.ts b/main/utils/formats.ts new file mode 100644 index 000000000..8a157ee93 --- /dev/null +++ b/main/utils/formats.ts @@ -0,0 +1,13 @@ +import {Format} from '../common/types'; + +const formats = new Map([ + [Format.gif, 'GIF'], + [Format.mp4, 'MP4 (H264)'], + [Format.av1, 'MP4 (AV1)'], + [Format.webm, 'WebM'], + [Format.apng, 'APNG'] +]); + +export const prettifyFormat = (format: Format): string => { + return formats.get(format)!; +}; diff --git a/main/utils/fps.js b/main/utils/fps.js deleted file mode 100644 index 35ff0fa6f..000000000 --- a/main/utils/fps.js +++ /dev/null @@ -1,13 +0,0 @@ -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const util = require('electron-util'); -const execa = require('execa'); - -const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); - -module.exports = async filePath => { - try { - await execa(ffmpegPath, ['-i', filePath]); - } catch (error) { - return /.*, (.*) fp.*/.exec(error.stderr)[1]; - } -}; diff --git a/main/utils/fps.ts b/main/utils/fps.ts new file mode 100644 index 000000000..9fb50ce24 --- /dev/null +++ b/main/utils/fps.ts @@ -0,0 +1,16 @@ +import util from 'electron-util'; +import execa from 'execa'; + +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); +const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); + +const getFps = async (filePath: string) => { + try { + await execa(ffmpegPath, ['-i', filePath]); + return undefined; + } catch (error) { + return /.*, (.*) fp.*/.exec(error.stderr)?.[1]; + } +}; + +export default getFps; diff --git a/main/utils/icon.js b/main/utils/icon.ts similarity index 61% rename from main/utils/icon.js rename to main/utils/icon.ts index 854a7b31f..d11ed30e0 100644 --- a/main/utils/icon.js +++ b/main/utils/icon.ts @@ -1,12 +1,7 @@ -'use strict'; const fileIcon = require('file-icon'); -const getAppIcon = async () => { +export const getAppIcon = async (): Promise => { const buffer = await fileIcon.buffer(process.pid); return buffer.toString('base64'); }; - -module.exports = { - getAppIcon -}; diff --git a/main/utils/image-preview.ts b/main/utils/image-preview.ts new file mode 100644 index 000000000..37b46ffb5 --- /dev/null +++ b/main/utils/image-preview.ts @@ -0,0 +1,59 @@ +/* eslint-disable array-element-newline */ + +import {BrowserWindow, dialog} from 'electron'; +import util from 'electron-util'; +import execa from 'execa'; +import tempy from 'tempy'; +import {promisify} from 'util'; +import type {Video} from '../video'; +import {generateTimestampedName} from './timestamped-name'; + +const base64Img = require('base64-img'); +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); +const ffmpegPath = util.fixPathForAsarUnpack(ffmpeg.path); + +const getBase64 = promisify(base64Img.base64); + +export const generatePreviewImage = async (filePath: string): Promise<{path: string; data: string} | undefined> => { + const previewPath = tempy.file({extension: '.jpg'}); + + try { + await execa(ffmpegPath, [ + '-ss', '0', + '-i', filePath, + '-t', '1', + '-vframes', '1', + '-f', 'image2', + previewPath + ]); + } catch { + return; + } + + try { + return { + path: previewPath, + data: await getBase64(previewPath) + }; + } catch { + return { + path: previewPath, + data: '' + }; + } +}; + +export const saveSnapshot = async (video: Video, time: number) => { + const {filePath: outputPath} = await dialog.showSaveDialog(BrowserWindow.getFocusedWindow()!, { + defaultPath: generateTimestampedName('Snapshot', '.jpg') + }); + + if (outputPath) { + await execa(ffmpegPath, [ + '-i', video.filePath, + '-ss', time.toString(), + '-vframes', '1', + outputPath + ]); + } +}; diff --git a/main/utils/macos-version.js b/main/utils/macos-version.js deleted file mode 100644 index 4448a987e..000000000 --- a/main/utils/macos-version.js +++ /dev/null @@ -1,3 +0,0 @@ -const macosVersion = require('macos-version'); - -module.exports = macosVersion; diff --git a/main/utils/notifications.ts b/main/utils/notifications.ts new file mode 100644 index 000000000..202af8f32 --- /dev/null +++ b/main/utils/notifications.ts @@ -0,0 +1,70 @@ +import {Notification, NotificationConstructorOptions, NotificationAction, app} from 'electron'; + +// Need to persist the notifications, otherwise it is garbage collected and the actions don't trigger +// https://github.com/electron/electron/issues/12690 +const notifications = new Set(); + +interface Action extends NotificationAction { + action?: () => void | Promise; +} + +interface NotificationOptions extends NotificationConstructorOptions { + actions?: Action[]; + click?: () => void | Promise; + show?: boolean; +} + +type NotificationPromise = Promise & { + show: () => void; + close: () => void; +}; + +export const notify = (options: NotificationOptions): NotificationPromise => { + const notification = new Notification(options); + + notifications.add(notification); + + const promise = new Promise(resolve => { + if (options.click && typeof options.click === 'function') { + notification.on('click', () => { + resolve(options.click?.()); + }); + } + + if (options.actions && options.actions.length > 0) { + notification.on('action', (_, index) => { + const button = options.actions?.[index]; + + if (button?.action && typeof button?.action === 'function') { + resolve(button?.action?.()); + } else { + resolve(index); + } + }); + } + + notification.on('close', () => { + resolve(undefined); + }); + }); + + promise.then(() => { + notifications.delete(notification); + }); + + (promise as NotificationPromise).show = () => { + notification.show(); + }; + + (promise as NotificationPromise).close = () => { + notification.close(); + }; + + if (options.show ?? true) { + notification.show(); + } + + return promise as NotificationPromise; +}; + +notify.simple = (text: string) => notify({title: app.name, body: text}); diff --git a/main/utils/open-files.js b/main/utils/open-files.js deleted file mode 100644 index a43b33bdd..000000000 --- a/main/utils/open-files.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; -const path = require('path'); - -const {supportedVideoExtensions} = require('../common/constants'); -const {openEditorWindow} = require('../editor'); -const {getEncoding, convertToH264} = require('./encoding'); - -const fileExtensions = supportedVideoExtensions.map(ext => `.${ext}`); - -const openFiles = (...filePaths) => { - return Promise.all( - filePaths - .filter(filePath => fileExtensions.includes(path.extname(filePath).toLowerCase())) - .map(async filePath => { - const encoding = await getEncoding(filePath); - if (encoding.toLowerCase() === 'hevc') { - openEditorWindow(await convertToH264(filePath), {originalFilePath: filePath}); - } else { - openEditorWindow(filePath); - } - }) - ); -}; - -module.exports = openFiles; diff --git a/main/utils/open-files.ts b/main/utils/open-files.ts new file mode 100644 index 000000000..73e2f0e6a --- /dev/null +++ b/main/utils/open-files.ts @@ -0,0 +1,19 @@ +'use strict'; +import path from 'path'; +import {supportedVideoExtensions} from '../common/constants'; +import {Video} from '../video'; + +const fileExtensions = supportedVideoExtensions.map(ext => `.${ext}`); + +export const openFiles = async (...filePaths: string[]) => { + return Promise.all( + filePaths + .filter(filePath => fileExtensions.includes(path.extname(filePath).toLowerCase())) + .map(async filePath => { + return Video.getOrCreate({ + filePath + }).openEditorWindow(); + }) + ); +}; + diff --git a/main/utils/plugin-config.js b/main/utils/plugin-config.js deleted file mode 100644 index 65260038a..000000000 --- a/main/utils/plugin-config.js +++ /dev/null @@ -1,67 +0,0 @@ -const Store = require('electron-store'); -const Ajv = require('./ajv'); - -class PluginConfig extends Store { - constructor(plugin) { - const defaults = {}; - - const validators = plugin.allServices.filter(({config}) => Boolean(config)).map(service => { - const schemaProps = JSON.parse(JSON.stringify(service.config)); - const requiredKeys = []; - for (const key of Object.keys(schemaProps)) { - if (!schemaProps[key].title) { - throw new Error('Config schema items should have a `title`'); - } - - if (schemaProps[key].required === true) { - delete schemaProps[key].required; - requiredKeys.push(key); - } - } - - const schema = { - type: 'object', - properties: schemaProps, - required: requiredKeys - }; - - const ajv = new Ajv({ - format: 'full', - useDefaults: true, - errorDataPath: 'property', - allErrors: true - }); - - const validator = ajv.compile(schema); - - validator(defaults); - validator.title = service.title; - validator.description = service.configDescription; - validator.config = service.config; - - return validator; - }); - - super({ - name: plugin.name, - cwd: 'plugins', - defaults - }); - - this.servicesWithNoConfig = plugin.allServices.filter(({config}) => !config); - this.validators = validators; - } - - isConfigValid() { - return this.validators.reduce((isValid, validator) => isValid && validator(this.store), true); - } - - get validServices() { - return [ - ...this.validators.filter(validator => validator(this.store)), - ...this.servicesWithNoConfig - ].map(service => service.title); - } -} - -module.exports = PluginConfig; diff --git a/main/utils/routes.js b/main/utils/routes.js deleted file mode 100644 index 07e15019b..000000000 --- a/main/utils/routes.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -const {app} = require('electron'); -const {is} = require('electron-util'); - -const loadRoute = (win, routeName, {openDevTools} = {}) => { - if (is.development) { - win.loadURL(`http://localhost:8000/${routeName}`); - win.openDevTools({mode: 'detach'}); - } else { - win.loadFile(`${app.getAppPath()}/renderer/out/${routeName}.html`); - if (openDevTools) { - win.openDevTools({mode: 'detach'}); - } - } -}; - -module.exports = loadRoute; diff --git a/main/utils/routes.ts b/main/utils/routes.ts new file mode 100644 index 000000000..f456637e0 --- /dev/null +++ b/main/utils/routes.ts @@ -0,0 +1,14 @@ +import {app, BrowserWindow} from 'electron'; +import {is} from 'electron-util'; + +export const loadRoute = (window: BrowserWindow, routeName: string, {openDevTools}: {openDevTools?: boolean} = {}) => { + if (is.development) { + window.loadURL(`http://localhost:8000/${routeName}`); + window.webContents.openDevTools({mode: 'detach'}); + } else { + window.loadFile(`${app.getAppPath()}/renderer/out/${routeName}.html`); + if (openDevTools) { + window.webContents.openDevTools({mode: 'detach'}); + } + } +}; diff --git a/main/utils/sentry.js b/main/utils/sentry.js deleted file mode 100644 index f73535283..000000000 --- a/main/utils/sentry.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -const {app} = require('electron'); -const {is} = require('electron-util'); -const Sentry = require('@sentry/electron'); -const settings = require('../common/settings'); - -const SENTRY_PUBLIC_DSN = 'https://2dffdbd619f34418817f4db3309299ce@sentry.io/255536'; - -const isSentryEnabled = !is.development && settings.get('allowAnalytics'); - -if (isSentryEnabled) { - const release = `${app.name}@${app.getVersion()}`.toLowerCase(); - Sentry.init({ - dsn: SENTRY_PUBLIC_DSN, - release - }); -} - -module.exports = Sentry; -module.exports.isSentryEnabled = isSentryEnabled; diff --git a/main/utils/sentry.ts b/main/utils/sentry.ts new file mode 100644 index 000000000..4ca7c6c23 --- /dev/null +++ b/main/utils/sentry.ts @@ -0,0 +1,20 @@ +'use strict'; + +import {app} from 'electron'; +import {is} from 'electron-util'; +import * as Sentry from '@sentry/electron'; +import {settings} from '../common/settings'; + +const SENTRY_PUBLIC_DSN = 'https://2dffdbd619f34418817f4db3309299ce@sentry.io/255536'; + +export const isSentryEnabled = !is.development && settings.get('allowAnalytics'); + +if (isSentryEnabled) { + const release = `${app.name}@${app.getVersion()}`.toLowerCase(); + Sentry.init({ + dsn: SENTRY_PUBLIC_DSN, + release + }); +} + +export default Sentry; diff --git a/main/utils/shortcut-to-accelerator.js b/main/utils/shortcut-to-accelerator.ts similarity index 80% rename from main/utils/shortcut-to-accelerator.js rename to main/utils/shortcut-to-accelerator.ts index 1926ea618..9876e47fd 100644 --- a/main/utils/shortcut-to-accelerator.js +++ b/main/utils/shortcut-to-accelerator.ts @@ -1,4 +1,5 @@ -const shortcutToAccelerator = shortcut => { + +export const shortcutToAccelerator = (shortcut: any) => { const {metaKey, altKey, ctrlKey, shiftKey, character} = shortcut; if (!character) { throw new Error(`shortcut needs character ${JSON.stringify(shortcut)}`); @@ -13,5 +14,3 @@ const shortcutToAccelerator = shortcut => { ].filter(Boolean); return keys.join('+'); }; - -module.exports = shortcutToAccelerator; diff --git a/main/utils/timestamped-name.js b/main/utils/timestamped-name.js deleted file mode 100644 index 079e438d8..000000000 --- a/main/utils/timestamped-name.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -const moment = require('moment'); - -module.exports = { - generateTimestampedName: (title = 'New Recording', extension = '') => `${title} ${moment().format('YYYY-MM-DD')} at ${moment().format('HH.mm.ss')}${extension}` -}; diff --git a/main/utils/timestamped-name.ts b/main/utils/timestamped-name.ts new file mode 100644 index 000000000..d61105cb5 --- /dev/null +++ b/main/utils/timestamped-name.ts @@ -0,0 +1,3 @@ +import moment from 'moment'; + +export const generateTimestampedName = (title = 'Kapture', extension = '') => `${title} ${moment().format('YYYY-MM-DD')} at ${moment().format('HH.mm.ss')}${extension}`; diff --git a/main/common/windows.js b/main/utils/windows.ts similarity index 56% rename from main/common/windows.js rename to main/utils/windows.ts index 52bf5a815..1c6241402 100644 --- a/main/common/windows.js +++ b/main/utils/windows.ts @@ -1,42 +1,59 @@ -'use strict'; +import {Menu, MenuItem, nativeImage} from 'electron'; +import Store from 'electron-store'; +import {windowManager} from '../windows/manager'; -const {Menu, MenuItem, nativeImage} = require('electron'); const {getWindows, activateWindow} = require('mac-windows'); const {getAppIconListByPid} = require('node-mac-app-icon'); -const Store = require('electron-store'); -const {selectApp} = require('../cropper'); +export interface MacWindow { + pid: number; + ownerName: string; + name: string; + width: number; + height: number; + x: number; + y: number; + number: number; +} const APP_BLACKLIST = [ 'Kap', 'Kap Beta' ]; -const store = new Store({ +const store = new Store<{ + appUsageHistory: Record; +}>({ name: 'usage-history' }); const usageHistory = store.get('appUsageHistory', {}); -const isValidApp = ({ownerName}) => !APP_BLACKLIST.includes(ownerName); +const isValidApp = ({ownerName}: MacWindow) => !APP_BLACKLIST.includes(ownerName); const getWindowList = async () => { - const windows = await getWindows(); + const windows = await getWindows() as MacWindow[]; const images = await getAppIconListByPid(windows.map(win => win.pid), { size: 16, failOnError: false - }); + }) as Array<{ + pid: number; + icon: Buffer; + }>; let maxLastUsed = 0; return windows.filter(window => isValidApp(window)).map(win => { const iconImage = images.find(img => img.pid === win.pid); - const icon = iconImage.icon ? nativeImage.createFromBuffer(iconImage.icon) : null; + const icon = iconImage?.icon ? nativeImage.createFromBuffer(iconImage.icon) : undefined; const window = { ...win, icon2x: icon, - icon: icon ? icon.resize({width: 16, height: 16}) : null, + icon: icon?.resize({width: 16, height: 16}), count: 0, lastUsed: 0, ...usageHistory[win.pid] @@ -57,7 +74,7 @@ const getWindowList = async () => { }); }; -const buildWindowsMenu = async selected => { +export const buildWindowsMenu = async (selected: string) => { const menu = new Menu(); const windows = await getWindowList(); @@ -68,7 +85,9 @@ const buildWindowsMenu = async selected => { icon: win.icon, type: 'checkbox', checked: win.ownerName === selected, - click: () => activateApp(win) + click: () => { + activateApp(win); + } }) ); } @@ -76,8 +95,8 @@ const buildWindowsMenu = async selected => { return menu; }; -const updateAppUsageHistory = app => { - const {count = 0} = usageHistory[app.pid] || {}; +const updateAppUsageHistory = (app: MacWindow) => { + const {count = 0} = usageHistory[app.pid] ?? {}; usageHistory[app.pid] = { count: count + 1, @@ -87,12 +106,7 @@ const updateAppUsageHistory = app => { store.set('appUsageHistory', usageHistory); }; -const activateApp = window => { +export const activateApp = (window: MacWindow) => { updateAppUsageHistory(window); - selectApp(window, activateWindow); -}; - -module.exports = { - buildWindowsMenu, - activateApp + windowManager.cropper?.selectApp(window, activateWindow); }; diff --git a/main/video.ts b/main/video.ts new file mode 100644 index 000000000..3475b521a --- /dev/null +++ b/main/video.ts @@ -0,0 +1,165 @@ +import path from 'path'; +import getFps from './utils/fps'; +import {getEncoding, convertToH264} from './utils/encoding'; +import {nativeImage, NativeImage, screen} from 'electron'; +import {ApertureOptions, Encoding} from './common/types'; +import {generateTimestampedName} from './utils/timestamped-name'; +import fs from 'fs'; +import {generatePreviewImage} from './utils/image-preview'; +import {windowManager} from './windows/manager'; + +interface VideoOptions { + filePath: string; + title?: string; + fps?: number; + encoding?: Encoding; + previewPath?: string; + pixelDensity?: number; + isNewRecording?: boolean; +} + +export class Video { + static all = new Map(); + + filePath: string; + title: string; + fps?: number; + encoding?: Encoding; + pixelDensity: number; + previewPath?: string; + dragIcon?: NativeImage; + isNewRecording = false; + isReady = false; + previewImage?: {path: string; data: string}; + + private readonly readyPromise: Promise; + private readonly previewReadyPromise: Promise; + + constructor(options: VideoOptions) { + this.filePath = options.filePath; + this.title = options.title ?? path.parse(this.filePath).name; + this.fps = options.fps; + this.encoding = options.encoding; + this.pixelDensity = options.pixelDensity ?? 1; + this.isNewRecording = options.isNewRecording ?? false; + this.previewPath = options.previewPath; + + Video.all.set(this.filePath, this); + + this.readyPromise = this.collectInfo(); + this.previewReadyPromise = this.readyPromise.then(async () => this.getPreviewPath()); + } + + static fromId(id: string) { + return this.all.get(id); + } + + static getOrCreate(options: VideoOptions) { + return Video.fromId(options.filePath) ?? new Video(options); + } + + async getFps() { + if (!this.fps) { + this.fps = Math.round(Number.parseFloat((await getFps(this.filePath)) ?? '0')); + } + + return this.fps; + } + + async exists() { + try { + await fs.promises.access(this.filePath, fs.constants.F_OK); + return true; + } catch { + return false; + } + } + + async getEncoding() { + if (!this.encoding) { + this.encoding = (await getEncoding(this.filePath)) as Encoding; + } + + return this.encoding; + } + + async getPreviewPath() { + if (!await this.exists()) { + return; + } + + if (!this.previewPath) { + if (this.encoding === 'h264') { + this.previewPath = this.filePath; + } else { + this.previewPath = await convertToH264(this.filePath); + } + } + + return this.previewPath; + } + + async getDragIcon({width, height}: {width: number; height: number}) { + const previewImagePath = (await this.generatePreviewImage())?.path; + + if (previewImagePath) { + const resizeOptions = width > height ? {width: 64} : {height: 64}; + return nativeImage.createFromPath(previewImagePath).resize(resizeOptions); + } + + return nativeImage.createEmpty(); + } + + async generatePreviewImage() { + if (!this.previewImage) { + this.previewImage = await generatePreviewImage(this.filePath); + } + + return this.previewImage; + } + + async whenReady() { + return this.readyPromise; + } + + async whenPreviewReady() { + return this.previewReadyPromise; + } + + async openEditorWindow() { + return windowManager.editor?.open(this); + } + + private async collectInfo() { + if (!await this.exists()) { + return; + } + + await Promise.all([ + this.getFps(), + this.getEncoding() + ]); + + this.isReady = true; + } +} + +export class Recording extends Video { + apertureOptions: ApertureOptions; + + constructor(options: VideoOptions & { apertureOptions: ApertureOptions }) { + const displays = screen.getAllDisplays(); + const pixelDensity = displays.find(display => display.id === options.apertureOptions.screenId)?.scaleFactor; + + super({ + filePath: options.filePath, + title: options.title ?? generateTimestampedName(), + fps: options.apertureOptions.fps, + encoding: options.apertureOptions.videoCodec ?? Encoding.h264, + pixelDensity + }); + + this.apertureOptions = options.apertureOptions; + this.isNewRecording = true; + } +} diff --git a/main/config.js b/main/windows/config.ts similarity index 68% rename from main/config.js rename to main/windows/config.ts index eac0ec1a4..05e61d8bb 100644 --- a/main/config.js +++ b/main/windows/config.ts @@ -1,15 +1,14 @@ 'use strict'; -const {BrowserWindow} = require('electron'); -const {ipcMain: ipc} = require('electron-better-ipc'); -const pEvent = require('p-event'); +import {BrowserWindow} from 'electron'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import pEvent from 'p-event'; -const loadRoute = require('./utils/routes'); -const {openPrefsWindow} = require('./preferences'); -const {getEditor} = require('./editor'); +import {loadRoute} from '../utils/routes'; +import {windowManager} from './manager'; -const openConfigWindow = async pluginName => { - const prefsWindow = await openPrefsWindow(); +const openConfigWindow = async (pluginName: string) => { + const prefsWindow = await windowManager.preferences?.open(); const configWindow = new BrowserWindow({ width: 320, height: 436, @@ -37,7 +36,7 @@ const openConfigWindow = async pluginName => { await pEvent(configWindow, 'closed'); }; -const openEditorConfigWindow = async (pluginName, serviceTitle, editorWindow) => { +const openEditorConfigWindow = async (pluginName: string, serviceTitle: string, editorWindow: BrowserWindow) => { const configWindow = new BrowserWindow({ width: 480, height: 420, @@ -65,10 +64,10 @@ const openEditorConfigWindow = async (pluginName, serviceTitle, editorWindow) => await pEvent(configWindow, 'closed'); }; -ipc.answerRenderer('open-edit-config', async ({pluginName, serviceTitle, filePath}) => { - return openEditorConfigWindow(pluginName, serviceTitle, getEditor(filePath)); +ipc.answerRenderer('open-edit-config', async ({pluginName, serviceTitle}, window) => { + return openEditorConfigWindow(pluginName, serviceTitle, window); }); -module.exports = { - openConfigWindow -}; +windowManager.setConfig({ + open: openConfigWindow +}); diff --git a/main/cropper.js b/main/windows/cropper.ts similarity index 67% rename from main/cropper.js rename to main/windows/cropper.ts index a4c86fab8..5bfaae173 100644 --- a/main/cropper.js +++ b/main/windows/cropper.ts @@ -1,22 +1,18 @@ -'use strict'; -const electron = require('electron'); -const delay = require('delay'); +import {windowManager} from './manager'; +import {BrowserWindow, systemPreferences, dialog, screen, Display, app} from 'electron'; +import delay from 'delay'; -const settings = require('./common/settings'); -const {hasMicrophoneAccess, ensureMicrophonePermissions, openSystemPreferences, ensureScreenCapturePermissions} = require('./common/system-permissions'); -const loadRoute = require('./utils/routes'); -const {checkForAnyBlockingEditors} = require('./editor'); +import {settings} from '../common/settings'; +import {hasMicrophoneAccess, ensureMicrophonePermissions, openSystemPreferences, ensureScreenCapturePermissions} from '../common/system-permissions'; +import {loadRoute} from '../utils/routes'; +import {MacWindow} from '../utils/windows'; -const {BrowserWindow, systemPreferences, dialog} = electron; - -const croppers = new Map(); -let notificationId = null; +const croppers = new Map(); +let notificationId: number | undefined; let isOpen = false; const closeAllCroppers = () => { - const {screen} = electron; - screen.removeAllListeners('display-removed'); screen.removeAllListeners('display-added'); @@ -27,13 +23,13 @@ const closeAllCroppers = () => { isOpen = false; - if (notificationId !== null) { + if (notificationId !== undefined) { systemPreferences.unsubscribeWorkspaceNotification(notificationId); - notificationId = null; + notificationId = undefined; } }; -const openCropper = (display, activeDisplayId) => { +const openCropper = (display: Display, activeDisplayId?: number) => { const {id, bounds} = display; const {x, y, width, height} = bounds; @@ -45,7 +41,7 @@ const openCropper = (display, activeDisplayId) => { hasShadow: false, enableLargerThanScreen: true, resizable: false, - moveable: false, + movable: false, frame: false, transparent: true, show: false, @@ -71,7 +67,9 @@ const openCropper = (display, activeDisplayId) => { if (isActive) { const savedCropper = settings.get('cropper', {}); + // @ts-expect-error if (savedCropper.displayId === id) { + // @ts-expect-error displayInfo.cropper = savedCropper; } } @@ -86,7 +84,7 @@ const openCropper = (display, activeDisplayId) => { const openCropperWindow = async () => { closeAllCroppers(); - if (checkForAnyBlockingEditors()) { + if (windowManager.editor?.areAnyBlocking()) { return; } @@ -108,7 +106,7 @@ const openCropperWindow = async () => { }); if (response === 0) { - openSystemPreferences(); + openSystemPreferences('Privacy_Microphone'); return false; } @@ -127,7 +125,6 @@ const openCropperWindow = async () => { isOpen = true; - const {screen} = electron; const displays = screen.getAllDisplays(); const activeDisplayId = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).id; @@ -139,17 +136,24 @@ const openCropperWindow = async () => { cropper.showInactive(); } - croppers.get(activeDisplayId).focus(); - notificationId = systemPreferences.subscribeWorkspaceNotification('NSWorkspaceActiveSpaceDidChangeNotification', () => { + croppers.get(activeDisplayId)?.focus(); + + // Electron typing issue, this should be marked as returning a number + notificationId = (systemPreferences as any).subscribeWorkspaceNotification('NSWorkspaceActiveSpaceDidChangeNotification', () => { closeAllCroppers(); }); - screen.on('display-removed', (event, oldDisplay) => { + screen.on('display-removed', (_, oldDisplay) => { const {id} = oldDisplay; const cropper = croppers.get(id); + if (!cropper) { + return; + } + const wasFocused = cropper.isFocused(); + // @ts-expect-error cropper.removeAllListeners('closed'); cropper.destroy(); croppers.delete(id); @@ -157,27 +161,27 @@ const openCropperWindow = async () => { if (wasFocused) { const activeDisplayId = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()).id; if (croppers.has(activeDisplayId)) { - croppers.get(activeDisplayId).focus(); + croppers.get(activeDisplayId)?.focus(); } } }); - screen.on('display-added', (event, newDisplay) => { + screen.on('display-added', (_, newDisplay) => { const cropper = openCropper(newDisplay); cropper.showInactive(); }); }; -const preventDefault = event => event.preventDefault(); +const preventDefault = (event: any) => event.preventDefault(); -const selectApp = async (window, activateWindow) => { +const selectApp = async (window: MacWindow, activateWindow: (ownerName: string) => Promise) => { for (const cropper of croppers.values()) { + // @ts-expect-error cropper.prependListener('blur', preventDefault); } await activateWindow(window.ownerName); - const {screen} = electron; const {x, y, width, height, ownerName} = window; const display = screen.getDisplayMatching({x, y, width, height}); @@ -191,9 +195,9 @@ const selectApp = async (window, activateWindow) => { cropper.webContents.send('blur'); } - croppers.get(id).focus(); + croppers.get(id)?.focus(); - croppers.get(id).webContents.send('select-app', { + croppers.get(id)?.webContents.send('select-app', { ownerName, x: x - screenX, y: y - screenY, @@ -203,12 +207,13 @@ const selectApp = async (window, activateWindow) => { }; const disableCroppers = () => { - if (notificationId !== null) { + if (notificationId !== undefined) { systemPreferences.unsubscribeWorkspaceNotification(notificationId); - notificationId = null; + notificationId = undefined; } for (const cropper of croppers.values()) { + // @ts-expect-error cropper.removeAllListeners('blur'); cropper.setIgnoreMouseEvents(true); cropper.setVisibleOnAllWorkspaces(true); @@ -223,11 +228,19 @@ const setRecordingCroppers = () => { const isCropperOpen = () => isOpen; -module.exports = { - openCropperWindow, - closeAllCroppers, +app.on('before-quit', closeAllCroppers); + +app.on('browser-window-created', () => { + if (!isCropperOpen()) { + app.dock.show(); + } +}); + +windowManager.setCropper({ + open: openCropperWindow, + close: closeAllCroppers, selectApp, - setRecordingCroppers, - disableCroppers, - isCropperOpen -}; + setRecording: setRecordingCroppers, + isOpen: isCropperOpen, + disable: disableCroppers +}); diff --git a/main/dialog.js b/main/windows/dialog.ts similarity index 65% rename from main/dialog.js rename to main/windows/dialog.ts index 365573f49..c77118f06 100644 --- a/main/dialog.js +++ b/main/windows/dialog.ts @@ -1,11 +1,16 @@ 'use strict'; -const {BrowserWindow} = require('electron'); -const {ipcMain: ipc} = require('electron-better-ipc'); +import {BrowserWindow, Rectangle} from 'electron'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import {loadRoute} from '../utils/routes'; +import {windowManager} from './manager'; -const loadRoute = require('./utils/routes'); +const DIALOG_MIN_WIDTH = 420; +const DIALOG_MIN_HEIGHT = 150; -const showDialog = options => new Promise(resolve => { +export type DialogOptions = any; + +const showDialog = async (options: DialogOptions) => new Promise(resolve => { const dialogWindow = new BrowserWindow({ width: 1, height: 1, @@ -26,12 +31,12 @@ const showDialog = options => new Promise(resolve => { loadRoute(dialogWindow, 'dialog'); - let buttons; + let buttons: any[]; let wasActionTaken; - const updateUi = async newOptions => { + const updateUi = async (newOptions: DialogOptions) => { wasActionTaken = true; - buttons = newOptions.buttons.map(button => { + buttons = newOptions.buttons.map((button: any) => { if (typeof button === 'string') { return {label: button}; } @@ -41,7 +46,7 @@ const showDialog = options => new Promise(resolve => { const cancelButton = buttons.findIndex(({label}) => label === 'Cancel'); - const {width, height} = await ipc.callRenderer(dialogWindow, 'data', { + const {width, height} = await ipc.callRenderer(dialogWindow, 'data', { cancelId: cancelButton > 0 ? cancelButton : undefined, ...options, ...newOptions, @@ -53,12 +58,12 @@ const showDialog = options => new Promise(resolve => { const titleBarHeight = dialogWindow.getSize()[1] - dialogWindow.getContentSize()[1]; dialogWindow.setBounds({ - width: Math.max(width, bounds.width), - height: Math.max(height + titleBarHeight, bounds.height) + width: Math.max(width, bounds.width, DIALOG_MIN_WIDTH), + height: Math.max(height + titleBarHeight, bounds.height, DIALOG_MIN_HEIGHT) }); }; - const unsubscribe = ipc.answerRenderer(`dialog-action-${dialogWindow.id}`, async index => { + const unsubscribe = ipc.answerRenderer(`dialog-action-${dialogWindow.id}`, async (index: number) => { if (buttons[index]) { if (buttons[index].action) { wasActionTaken = false; @@ -75,7 +80,7 @@ const showDialog = options => new Promise(resolve => { } }); - const cleanup = value => { + const cleanup = (value?: number) => { wasActionTaken = true; unsubscribe(); dialogWindow.close(); @@ -88,6 +93,6 @@ const showDialog = options => new Promise(resolve => { }); }); -module.exports = { - showDialog -}; +windowManager.setDialog({ + open: showDialog +}); diff --git a/main/windows/editor.ts b/main/windows/editor.ts new file mode 100644 index 000000000..9b256852f --- /dev/null +++ b/main/windows/editor.ts @@ -0,0 +1,150 @@ +import {EditorWindowState} from '../common/types'; +import type {Video} from '../video'; +import KapWindow from './kap-window'; +import {MenuItemId} from '../menus/utils'; +import {BrowserWindow, dialog} from 'electron'; +import {is} from 'electron-util'; +import fs from 'fs'; +import {saveSnapshot} from '../utils/image-preview'; +import {windowManager} from './manager'; + +const pify = require('pify'); + +const OPTIONS_BAR_HEIGHT = 48; +const VIDEO_ASPECT = 9 / 16; +const MIN_VIDEO_WIDTH = 900; +const MIN_VIDEO_HEIGHT = MIN_VIDEO_WIDTH * VIDEO_ASPECT; +const MIN_WINDOW_HEIGHT = MIN_VIDEO_HEIGHT + OPTIONS_BAR_HEIGHT; + +const editors = new Map(); +const editorsWithNotSavedDialogs = new Map(); + +const open = async (video: Video) => { + if (editors.has(video.filePath)) { + editors.get(video.filePath).show(); + return; + } + + // TODO: Make this smarter so the editor can show with a spinner while the preview is generated for longer preview conversions (like ProRes) + await video.whenPreviewReady(); + + const editorKapWindow = new KapWindow({ + title: video.title, + // TODO: Return those to the original values when we are able to resize below min size + // Upstream issue: https://github.com/electron/electron/issues/27025 + // minWidth: MIN_VIDEO_WIDTH, + // minHeight: MIN_WINDOW_HEIGHT, + minWidth: 360, + minHeight: 392, + width: MIN_VIDEO_WIDTH, + height: MIN_WINDOW_HEIGHT, + backgroundColor: '#222222', + webPreferences: { + nodeIntegration: true, + webSecurity: !is.development // Disable webSecurity in dev to load video over file:// protocol while serving over insecure http, this is not needed in production where we use file:// protocol for html serving. + }, + frame: false, + transparent: true, + vibrancy: 'window', + route: 'editor', + initialState: { + previewFilePath: video.previewPath!, + filePath: video.filePath, + fps: video.fps!, + title: video.title + }, + menu: defaultMenu => { + if (!video.isNewRecording) { + return; + } + + const fileMenu = defaultMenu.find(item => item.id === MenuItemId.file); + + if (fileMenu) { + const submenu = fileMenu.submenu as Electron.MenuItemConstructorOptions[]; + + const index = submenu.findIndex(item => item.id === MenuItemId.openVideo); + + if (index > -1) { + submenu.splice(index + 1, 0, { + type: 'separator' + }, { + label: 'Save Original…', + id: MenuItemId.saveOriginal, + accelerator: 'Command+S', + click: async () => saveOriginal(video) + }); + } + } + } + }); + + const editorWindow = editorKapWindow.browserWindow; + + editors.set(video.filePath, editorWindow); + + if (video.isNewRecording) { + editorWindow.setDocumentEdited(true); + editorWindow.on('close', (event: any) => { + editorsWithNotSavedDialogs.set(video.filePath, true); + const buttonIndex = dialog.showMessageBoxSync(editorWindow, { + type: 'question', + buttons: [ + 'Discard', + 'Cancel' + ], + defaultId: 0, + cancelId: 1, + message: 'Are you sure that you want to discard this recording?', + detail: 'You will no longer be able to edit and export the original recording.' + }); + + if (buttonIndex === 1) { + event.preventDefault(); + } + + editorsWithNotSavedDialogs.delete(video.filePath); + }); + } + + editorWindow.on('closed', () => { + editors.delete(video.filePath); + }); + + editorWindow.on('blur', () => { + editorKapWindow.callRenderer('blur'); + }); + + editorWindow.on('focus', () => { + editorKapWindow.callRenderer('focus'); + }); + + editorKapWindow.answerRenderer('save-snapshot', (time: number) => { + saveSnapshot(video, time); + }); +}; + +const saveOriginal = async (video: Video) => { + const {filePath} = await dialog.showSaveDialog(BrowserWindow.getFocusedWindow()!, { + defaultPath: `${video.title}.mp4` + }); + + if (filePath) { + await pify(fs.copyFile)(video.filePath, filePath, fs.constants.COPYFILE_FICLONE); + } +}; + +const areAnyBlocking = () => { + if (editorsWithNotSavedDialogs.size > 0) { + const [path] = editorsWithNotSavedDialogs.keys(); + editors.get(path).focus(); + return true; + } + + return false; +}; + +windowManager.setEditor({ + open, + areAnyBlocking +}); diff --git a/main/exports.js b/main/windows/exports.ts similarity index 71% rename from main/exports.js rename to main/windows/exports.ts index 66de1735a..83026d104 100644 --- a/main/exports.js +++ b/main/windows/exports.ts @@ -1,11 +1,11 @@ 'use strict'; -const {BrowserWindow, ipcMain} = require('electron'); -const pEvent = require('p-event'); +import {BrowserWindow, ipcMain} from 'electron'; +import pEvent from 'p-event'; +import {loadRoute} from '../utils/routes'; +import {windowManager} from './manager'; -const loadRoute = require('./utils/routes'); - -let exportsWindow = null; +let exportsWindow: BrowserWindow | undefined; const openExportsWindow = async () => { if (exportsWindow) { @@ -34,7 +34,7 @@ const openExportsWindow = async () => { loadRoute(exportsWindow, 'exports'); exportsWindow.on('close', () => { - exportsWindow = null; + exportsWindow = undefined; }); await pEvent(ipcMain, 'exports-ready'); @@ -46,7 +46,7 @@ const openExportsWindow = async () => { const getExportsWindow = () => exportsWindow; -module.exports = { - openExportsWindow, - getExportsWindow -}; +windowManager.setExports({ + open: openExportsWindow, + get: getExportsWindow +}); diff --git a/main/windows/kap-window.ts b/main/windows/kap-window.ts new file mode 100644 index 000000000..b48f4346c --- /dev/null +++ b/main/windows/kap-window.ts @@ -0,0 +1,170 @@ +import electron, {app, BrowserWindow, Menu} from 'electron'; +import {ipcMain as ipc} from 'electron-better-ipc'; +import pEvent from 'p-event'; +import {customApplicationMenu, defaultApplicationMenu, MenuModifier} from '../menus/application'; +import {loadRoute} from '../utils/routes'; + +interface KapWindowOptions extends Electron.BrowserWindowConstructorOptions { + route: string; + waitForMount?: boolean; + initialState?: State; + menu?: MenuModifier; + dock?: boolean; +} + +// TODO: remove this when all windows use KapWindow +app.on('browser-window-focus', (_, window) => { + if (!KapWindow.fromId(window.id)) { + Menu.setApplicationMenu(Menu.buildFromTemplate(defaultApplicationMenu())); + } +}); + +// Has to be named BrowserWindow because of +// https://github.com/electron/electron/blob/master/lib/browser/api/browser-window.ts#L82 +export default class KapWindow { + static defaultOptions: Partial> = { + waitForMount: true, + dock: true, + menu: defaultMenu => defaultMenu + }; + + private static readonly windows = new Map(); + + browserWindow: BrowserWindow; + state?: State; + menu: Menu = Menu.buildFromTemplate(defaultApplicationMenu()); + + private readonly readyPromise: Promise; + private readonly cleanupMethods: Array<() => void> = []; + private readonly options: KapWindowOptions; + + constructor(props: KapWindowOptions) { + const { + route, + waitForMount, + initialState, + ...rest + } = props; + + this.browserWindow = new BrowserWindow({ + ...rest, + webPreferences: { + nodeIntegration: true, + ...rest.webPreferences + }, + show: false + }); + + this.cleanupMethods = []; + this.options = { + ...KapWindow.defaultOptions, + ...props + }; + + this.state = initialState; + this.generateMenu(); + loadRoute(this.browserWindow, route); + this.readyPromise = this.setupWindow(); + } + + static getAllWindows() { + return [...this.windows.values()]; + } + + static fromId(id: number) { + return this.windows.get(id); + } + + get id() { + return this.browserWindow.id; + } + + get webContents() { + return this.browserWindow.webContents; + } + + cleanup = () => { + this.executeIfNotDestroyed(() => { + KapWindow.windows.delete(this.id); + }); + + for (const method of this.cleanupMethods) { + method(); + } + }; + + callRenderer = async (channel: string, data?: T) => { + return ipc.callRenderer(this.browserWindow, channel, data); + }; + + answerRenderer = (channel: string, callback: (data: T, window: electron.BrowserWindow) => R) => { + this.cleanupMethods.push(ipc.answerRenderer(this.browserWindow, channel, callback)); + }; + + setState = (partialState: State) => { + this.state = { + ...this.state, + ...partialState + }; + + this.callRenderer('kap-window-state', this.state); + }; + + whenReady = async () => { + return this.readyPromise; + }; + + private readonly generateMenu = () => { + this.menu = Menu.buildFromTemplate( + customApplicationMenu(this.options.menu!) + ); + }; + + private async setupWindow() { + const {waitForMount} = this.options; + + KapWindow.windows.set(this.id, this); + + this.browserWindow.on('show', () => { + if (this.options.dock && !app.dock.isVisible) { + app.dock.show(); + } else if (!this.options.dock && app.dock.isVisible) { + app.dock.hide(); + } + }); + + this.browserWindow.on('close', this.cleanup); + this.browserWindow.on('closed', this.cleanup); + + this.browserWindow.on('focus', () => { + this.generateMenu(); + Menu.setApplicationMenu(this.menu); + }); + + this.webContents.on('did-finish-load', async () => { + if (this.state) { + this.callRenderer('kap-window-state', this.state); + } + }); + + if (waitForMount) { + return new Promise(resolve => { + this.answerRenderer('kap-window-mount', () => { + this.browserWindow.show(); + resolve(); + }); + }); + } + + await pEvent(this.webContents, 'did-finish-load'); + this.browserWindow.show(); + } + + // Use this around any call that causes: + // TypeError: Object has been destroyed + private readonly executeIfNotDestroyed = (callback: () => void) => { + if (!this.browserWindow.isDestroyed()) { + callback(); + } + }; +} diff --git a/main/windows/load.ts b/main/windows/load.ts new file mode 100644 index 000000000..9f52b620a --- /dev/null +++ b/main/windows/load.ts @@ -0,0 +1,6 @@ +import './editor'; +import './cropper'; +import './config'; +import './dialog'; +import './exports'; +import './preferences'; diff --git a/main/windows/manager.ts b/main/windows/manager.ts new file mode 100644 index 000000000..cfabcf7d9 --- /dev/null +++ b/main/windows/manager.ts @@ -0,0 +1,72 @@ +import type {BrowserWindow} from 'electron'; +import {MacWindow} from '../utils/windows'; +import type {Video} from '../video'; +import type {DialogOptions} from './dialog'; +import type {PreferencesWindowOptions} from './preferences'; + +export interface EditorManager { + open: (video: Video) => Promise; + areAnyBlocking: () => boolean; +} + +export interface CropperManager { + open: () => Promise; + close: () => void; + disable: () => void; + setRecording: () => void; + isOpen: () => boolean; + selectApp: (window: MacWindow, activateWindow: (ownerName: string) => Promise) => void; +} + +export interface ConfigManager { + open: (pluginName: string) => Promise; +} + +export interface DialogManager { + open: (options: DialogOptions) => Promise; +} + +export interface ExportsManager { + open: () => Promise; + get: () => BrowserWindow | undefined; +} + +export interface PreferencesManager { + open: (options?: PreferencesWindowOptions) => Promise; + close: () => void; +} + +export class WindowManager { + editor?: EditorManager; + cropper?: CropperManager; + config?: ConfigManager; + dialog?: DialogManager; + exports?: ExportsManager; + preferences?: PreferencesManager; + + setEditor = (editorManager: EditorManager) => { + this.editor = editorManager; + }; + + setCropper = (cropperManager: CropperManager) => { + this.cropper = cropperManager; + }; + + setConfig = (configManager: ConfigManager) => { + this.config = configManager; + }; + + setDialog = (dialogManager: DialogManager) => { + this.dialog = dialogManager; + }; + + setExports = (exportsManager: ExportsManager) => { + this.exports = exportsManager; + }; + + setPreferences = (preferencesManager: PreferencesManager) => { + this.preferences = preferencesManager; + }; +} + +export const windowManager = new WindowManager(); diff --git a/main/preferences.js b/main/windows/preferences.ts similarity index 65% rename from main/preferences.js rename to main/windows/preferences.ts index d2c12a1be..a144a265a 100644 --- a/main/preferences.js +++ b/main/windows/preferences.ts @@ -1,19 +1,19 @@ -'use strict'; +import {BrowserWindow} from 'electron'; +import {promisify} from 'util'; +import pEvent from 'p-event'; -const {BrowserWindow} = require('electron'); -const {promisify} = require('util'); -const pEvent = require('p-event'); +import {ipcMain as ipc} from 'electron-better-ipc'; +import {loadRoute} from '../utils/routes'; +import {track} from '../common/analytics'; +import {windowManager} from './manager'; -const {ipcMain: ipc} = require('electron-better-ipc'); -const {closeAllCroppers} = require('./cropper'); -const loadRoute = require('./utils/routes'); -const {track} = require('./common/analytics'); +let prefsWindow: BrowserWindow | undefined; -let prefsWindow = null; +export type PreferencesWindowOptions = any; -const openPrefsWindow = async options => { +const openPrefsWindow = async (options?: PreferencesWindowOptions) => { track('preferences/opened'); - closeAllCroppers(); + windowManager.cropper?.close(); if (prefsWindow) { if (options) { @@ -46,7 +46,7 @@ const openPrefsWindow = async options => { prefsWindow.setSheetOffset(titlebarHeight); prefsWindow.on('close', () => { - prefsWindow = null; + prefsWindow = undefined; }); loadRoute(prefsWindow, 'preferences'); @@ -59,6 +59,7 @@ const openPrefsWindow = async options => { ipc.callRenderer(prefsWindow, 'mount'); + // @ts-expect-error await promisify(ipc.answerRenderer)('preferences-ready'); prefsWindow.show(); @@ -73,7 +74,7 @@ const closePrefsWindow = () => { ipc.answerRenderer('open-preferences', openPrefsWindow); -module.exports = { - openPrefsWindow, - closePrefsWindow -}; +windowManager.setPreferences({ + open: openPrefsWindow, + close: closePrefsWindow +}); diff --git a/package.json b/package.json index ba513687c..690055471 100644 --- a/package.json +++ b/package.json @@ -12,16 +12,21 @@ "url": "https://wulkano.com" }, "private": true, - "main": "main/index.js", + "main": "dist-js/index.js", "scripts": { "lint": "xo", - "test": "xo && ava", - "start": "yarn && electron .", - "build": "next build renderer && next export renderer", + "lint:fix": "xo --fix", + "test:main": "TS_NODE_PROJECT=test/tsconfig.json ava", + "test": "yarn lint && yarn test:main", + "start": "tsc && run-electron .", + "build-main": "tsc", + "build-renderer": "next build renderer && next export renderer", + "build": "yarn build-main && yarn build-renderer", "dist": "npm run build && electron-builder", "pack": "npm run build && electron-builder --dir", "postinstall": "electron-builder install-app-deps", - "sentry-version": "echo \"$npm_package_name@$npm_package_version\"" + "sentry-version": "echo \"$npm_package_name@$npm_package_version\"", + "dev": "next dev renderer" }, "bundle": { "name": "Kap" @@ -36,12 +41,14 @@ "base64-img": "^1.0.4", "classnames": "^2.2.6", "clean-stack": "^3.0.0", + "cp-file": "^9.0.0", "delay": "^4.3.0", - "electron-better-ipc": "^1.0.1", + "electron-better-ipc": "^1.1.1", "electron-log": "^4.1.1", "electron-next": "^3.1.5", "electron-notarize": "^0.3.0", - "electron-store": "^5.1.1", + "electron-store": "^7.0.2", + "electron-timber": "^0.5.1", "electron-updater": "^4.3.1", "electron-util": "^0.14.1", "ensure-error": "^2.0.0", @@ -56,18 +63,20 @@ "mac-screen-capture-permissions": "^1.1.0", "mac-windows": "^0.7.1", "macos-audio-devices": "^1.3.2", - "macos-release": "^2.4.0", + "macos-release": "^2.4.1", "macos-version": "^5.2.0", "make-dir": "^3.1.0", "moment": "^2.24.0", "move-file": "^2.0.0", "nearest-normal-aspect-ratio": "^1.2.1", "node-mac-app-icon": "^1.4.0", + "object-hash": "^2.0.3", "p-cancelable": "^2.0.0", "p-event": "^4.1.0", "package-json": "^6.5.0", "pify": "^4.0.1", "plist": "^3.0.1", + "pretty-bytes": "^5.5.0", "pretty-ms": "^7.0.0", "prop-types": "^15.7.2", "react": "^16.13.1", @@ -80,42 +89,67 @@ "tildify": "^2.0.0", "tmp": "^0.2.0", "unstated": "^1.2.0", + "unstated-next": "^1.1.0", "yarn": "^1.22.4" }, "devDependencies": { - "ava": "^3.9.0", + "@babel/core": "^7.12.16", + "@babel/eslint-parser": "^7.12.16", + "@sindresorhus/tsconfig": "^0.7.0", + "@types/got": "9.6.0", + "@types/insight": "^0.8.0", + "@types/lodash": "^4.14.168", + "@types/module-alias": "^2.0.0", + "@types/node": "^14.11.10", + "@types/object-hash": "^1.3.4", + "@types/react": "^16.9.46", + "@types/sinon": "^9.0.10", + "@typescript-eslint/eslint-plugin": "^4.15.0", + "@typescript-eslint/parser": "^4.15.0", + "ava": "^3.15.0", "babel-eslint": "^10.1.0", "electron": "8.2.4", "electron-builder": "^22.6.0", "electron-builder-notarize": "^1.1.2", + "eslint-config-xo": "^0.34.0", "eslint-config-xo-react": "^0.23.0", - "eslint-plugin-react": "^7.19.0", - "eslint-plugin-react-hooks": "^3.0.0", + "eslint-config-xo-typescript": "^0.37.0", + "eslint-plugin-react": "^7.22.0", + "eslint-plugin-react-hooks": "^4.2.0", "husky": "^4.2.5", "module-alias": "^2.2.2", - "next": "^9.3.6", + "next": "^10.0.4", + "run-electron": "^1.0.0", "sinon": "^9.0.2", + "ts-node": "^9.1.1", + "type-fest": "^0.21.0", + "typed-emitter": "^1.3.1", + "typescript": "^4.0.3", "unique-string": "^2.0.0", - "xo": "^0.30.0" + "xo": "^0.37.1" }, "_moduleAliases": { - "electron": "test/mocks/electron.js" + "electron": "test/mocks/electron.ts" }, "ava": { "files": [ + "test/**/*.ts", "test/**/*.js", "!test/helpers", "!test/mocks" ], + "extensions": [ + "ts" + ], "verbose": true, "timeout": "5m", "failFast": true, "require": [ + "ts-node/register", "module-alias/register" ] }, "xo": { - "parser": "babel-eslint", "extends": "xo-react", "space": 2, "envs": [ @@ -123,20 +157,78 @@ "browser" ], "rules": { + "template-curly-spacing": "off", "import/no-extraneous-dependencies": "off", "import/no-unassigned-import": "off", "react/jsx-closing-tag-location": "off", "react/require-default-props": "off", "react/jsx-curly-brace-presence": "off", "react/static-property-placement": "off", + "react/react-in-jsx-scope": "off", "react/boolean-prop-naming": "off", "unicorn/prefer-set-has": "off", - "ava/use-test": "off" + "ava/use-test": "off", + "import/extensions": "off" }, "ignores": [ + "dist-js", "dist", "renderer/.next", - "renderer/out" + "renderer/out", + "renderer/next.config.js" + ], + "overrides": [ + { + "files": [ + "**/*.js", + "**/*.jsx" + ], + "parser": "babel-eslint" + }, + { + "files": [ + "**/*.ts", + "**/*.tsx" + ], + "extends": [ + "xo-react", + "xo-typescript" + ], + "parserOptions": { + "project": [ + "tsconfig.json", + "renderer/tsconfig.json", + "test/tsconfig.json" + ] + }, + "rules": { + "react-hooks/exhaustive-deps": "off", + "@typescript-eslint/no-dynamic-delete": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-implicit-any-catch": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "no-await-in-loop": "off", + "react/prop-types": "off", + "@typescript-eslint/no-confusing-void-expression": "off", + "@typescript-eslint/indent": [ + "error", + 2 + ] + } + }, + { + "files": [ + "test/**/*.ts" + ], + "rules": { + "@typescript-eslint/consistent-type-assertions": "off", + "@typescript-eslint/member-ordering": "off", + "import/no-anonymous-default-export": "off", + "@typescript-eslint/no-extraneous-class": "off", + "@typescript-eslint/no-empty-function": "off" + } + } ] }, "husky": { @@ -155,7 +247,8 @@ ] }, "files": [ - "**/*", + "static", + "dist-js/**/*", "!renderer", "renderer/out" ], diff --git a/plz.png b/plz.png new file mode 100644 index 000000000..130a56dcb Binary files /dev/null and b/plz.png differ diff --git a/renderer/common b/renderer/common new file mode 120000 index 000000000..3f2ec349e --- /dev/null +++ b/renderer/common @@ -0,0 +1 @@ +../main/common \ No newline at end of file diff --git a/renderer/components/action-bar/controls/advanced.js b/renderer/components/action-bar/controls/advanced.js index f2770ecf6..41e64bdc2 100644 --- a/renderer/components/action-bar/controls/advanced.js +++ b/renderer/components/action-bar/controls/advanced.js @@ -62,7 +62,7 @@ const AdvancedControls = {}; const stopPropagation = event => event.stopPropagation(); class Left extends React.Component { - state = {} + state = {}; select = React.createRef(); @@ -92,7 +92,7 @@ class Left extends React.Component { y: Math.round(top) + 6, positioningItem }); - } + }; render() { const {advanced, toggleAdvanced, toggleRatioLock, ratioLocked, ratio = []} = this.props; @@ -214,7 +214,7 @@ class Right extends React.Component { heightInput: heightInput.current.getRef(), ignoreEmpty }); - } + }; onHeightChange = (event, {ignoreEmpty} = {}) => { const {bounds, width, setBounds, ratioLocked, ratio, setHeight} = this.props; @@ -233,17 +233,17 @@ class Right extends React.Component { heightInput: heightInput.current.getRef(), ignoreEmpty }); - } + }; onWidthBlur = event => { this.onWidthChange(event, {ignoreEmpty: false}); handleWidthInput.flush(); - } + }; onHeightBlur = event => { this.onHeightChange(event, {ignoreEmpty: false}); handleHeightInput.flush(); - } + }; render() { const {swapDimensions, width, height, screenWidth, screenHeight, advanced} = this.props; @@ -262,7 +262,6 @@ class Right extends React.Component { tabIndex={advanced ? 0 : -1} onChange={this.onWidthChange} onBlur={this.onWidthBlur} - onKeyDown={this.onWidthChange} onMouseDown={stopPropagation} />
@@ -280,7 +279,6 @@ class Right extends React.Component { tabIndex={advanced ? 0 : -1} onChange={this.onHeightChange} onBlur={this.onHeightBlur} - onKeyDown={this.onHeightChange} onMouseDown={stopPropagation} /> {keyboardInputStyles} diff --git a/renderer/components/action-bar/controls/main.js b/renderer/components/action-bar/controls/main.js index 64f8c42bd..79284921c 100644 --- a/renderer/components/action-bar/controls/main.js +++ b/renderer/components/action-bar/controls/main.js @@ -28,7 +28,7 @@ const remote = electron.remote || false; let menu; const buildMenu = async ({selectedApp}) => { - const {buildWindowsMenu} = remote.require('./common/windows'); + const {buildWindowsMenu} = remote.require('./utils/windows'); menu = await buildWindowsMenu(selectedApp); }; @@ -86,9 +86,9 @@ MainControls.Left = connect( class Right extends React.Component { onCogMenuClick = async () => { - const cogMenu = await electron.remote.require('./menus').getCogMenu(); + const cogMenu = await electron.remote.require('./menus/cog').getCogMenu(); cogMenu.popup(); - } + }; render() { const {enterFullscreen, exitFullscreen, isFullscreen, advanced} = this.props; diff --git a/renderer/components/action-bar/index.js b/renderer/components/action-bar/index.js index 7d0c3ab36..31a4f2f1a 100644 --- a/renderer/components/action-bar/index.js +++ b/renderer/components/action-bar/index.js @@ -18,7 +18,7 @@ class ActionBar extends React.Component { height: 0, x: 0, y: 0 - } + }; render() { const { diff --git a/renderer/components/action-bar/record-button.js b/renderer/components/action-bar/record-button.js index 9a4e10221..f7480b958 100644 --- a/renderer/components/action-bar/record-button.js +++ b/renderer/components/action-bar/record-button.js @@ -6,7 +6,7 @@ import classNames from 'classnames'; import {connect, CropperContainer} from '../../containers'; import {handleKeyboardActivation} from '../../utils/inputs'; -const getMediaNode = deviceId => new Promise((resolve, reject) => { +const getMediaNode = async deviceId => new Promise((resolve, reject) => { navigator.getUserMedia({ audio: {deviceId} }, stream => { @@ -53,7 +53,7 @@ const RecordButton = ({ javascriptNode.onaudioprocess = () => { const array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(array); - + // eslint-disable-next-line unicorn/no-array-reduce const avg = array.reduce((p, c) => p + c) / array.length; if (avg >= 36) { setShowFirstRipple(true); @@ -98,7 +98,7 @@ const RecordButton = ({ if (cropperExists) { const {remote} = electron; - const {startRecording} = remote.require('./common/aperture'); + const {startRecording} = remote.require('./aperture'); willStartRecording(); diff --git a/renderer/components/cropper/cursor.js b/renderer/components/cropper/cursor.js index 01b333883..06eb86efa 100644 --- a/renderer/components/cropper/cursor.js +++ b/renderer/components/cropper/cursor.js @@ -6,7 +6,7 @@ import classNames from 'classnames'; import {connect, CursorContainer, CropperContainer} from '../../containers'; class Cursor extends React.Component { - remote = electron.remote || false + remote = electron.remote || false; render() { if (!this.remote) { diff --git a/renderer/components/cropper/handles.js b/renderer/components/cropper/handles.js index b7e3f5ccf..5d255766e 100644 --- a/renderer/components/cropper/handles.js +++ b/renderer/components/cropper/handles.js @@ -12,7 +12,7 @@ class Handle extends React.Component { left: false, right: false, ratioLocked: false - } + }; render() { const { @@ -94,7 +94,7 @@ class Handles extends React.Component { ratioLocked: false, width: 0, height: 0 - } + }; render() { const { diff --git a/renderer/components/cropper/overlay.js b/renderer/components/cropper/overlay.js index 580f10bb7..ce5b44cab 100644 --- a/renderer/components/cropper/overlay.js +++ b/renderer/components/cropper/overlay.js @@ -18,7 +18,7 @@ class Overlay extends React.Component { width: 0, height: 0, isReady: false - } + }; render() { const { diff --git a/renderer/components/dialog/actions.js b/renderer/components/dialog/actions.js index 51aa0d6ac..8e4f52c2a 100644 --- a/renderer/components/dialog/actions.js +++ b/renderer/components/dialog/actions.js @@ -26,7 +26,7 @@ const Actions = ({buttons, performAction, defaultId}) => { key={button.label} type="button" disabled={index === activeButton} - onClick={() => action(index)} + onClick={async () => action(index)} > {index === activeButton ? button.activeLabel || button.label : button.label} diff --git a/renderer/components/dialog/icon.js b/renderer/components/dialog/icon.js index 7344027f6..ab25d98fe 100644 --- a/renderer/components/dialog/icon.js +++ b/renderer/components/dialog/icon.js @@ -1,6 +1,6 @@ import React from 'react'; -export default () => { +const Icon = () => { return (
@@ -18,3 +18,5 @@ export default () => {
); }; + +export default Icon; diff --git a/renderer/components/editor/controls/left.js b/renderer/components/editor/controls/left.js deleted file mode 100644 index 5ff01ea7a..000000000 --- a/renderer/components/editor/controls/left.js +++ /dev/null @@ -1,61 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import {PlayIcon, PauseIcon} from '../../../vectors'; -import {connect, VideoContainer} from '../../../containers'; -import formatTime from '../../../utils/format-time'; - -class LeftControls extends React.Component { - render() { - const {play, pause, isPaused, currentTime} = this.props; - - return ( -
-
- { - isPaused ? - : - - } -
-
{formatTime(currentTime, {showMilliseconds: false})}
- -
- ); - } -} - -LeftControls.propTypes = { - play: PropTypes.elementType, - pause: PropTypes.elementType, - isPaused: PropTypes.bool, - currentTime: PropTypes.number -}; - -export default connect( - [VideoContainer], - ({isPaused, currentTime}) => ({isPaused, currentTime}), - ({play, pause}) => ({play, pause}) -)(LeftControls); diff --git a/renderer/components/editor/controls/left.tsx b/renderer/components/editor/controls/left.tsx new file mode 100644 index 000000000..a857a9642 --- /dev/null +++ b/renderer/components/editor/controls/left.tsx @@ -0,0 +1,47 @@ +import VideoControlsContainer from '../video-controls-container'; +import VideoTimeContainer from '../video-time-container'; +import {PlayIcon, PauseIcon} from '../../../vectors'; +import formatTime from '../../../utils/format-time'; + +const LeftControls = () => { + const {isPaused, play, pause} = VideoControlsContainer.useContainer(); + const {currentTime} = VideoTimeContainer.useContainer(); + + return ( +
+
+ { + isPaused ? + : + + } +
+
{formatTime(currentTime, {showMilliseconds: false})}
+ +
+ ); +}; + +export default LeftControls; diff --git a/renderer/components/editor/controls/play-bar.js b/renderer/components/editor/controls/play-bar.js deleted file mode 100644 index cb7f761bb..000000000 --- a/renderer/components/editor/controls/play-bar.js +++ /dev/null @@ -1,237 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import classNames from 'classnames'; - -import {connect, VideoContainer} from '../../../containers'; -import Preview from './preview'; - -class PlayBar extends React.Component { - state = { - hoverTime: 0 - }; - - progress = React.createRef(); - - getTimeFromEvent = event => { - const {startTime, endTime} = this.props; - - const cursorX = event.clientX; - const {x, width} = this.progress.current.getBoundingClientRect(); - - const percent = (cursorX - x) / width; - const time = startTime + ((endTime - startTime) * percent); - - return Math.max(0, time); - } - - seek = event => { - const {startTime, endTime, seek} = this.props; - const time = this.getTimeFromEvent(event); - - if (startTime <= time && time <= endTime) { - seek(time); - } - } - - updatePreview = event => { - const time = this.getTimeFromEvent(event); - this.setState({hoverTime: time}); - } - - startResizing = () => { - const {pause} = this.props; - this.setState({resizing: true}); - pause(); - } - - stopResizing = () => { - const {play} = this.props; - this.setState({resizing: false}); - play(); - } - - setStartTime = event => this.props.setStartTime(Number.parseFloat(event.target.value)) - - setEndTime = event => this.props.setEndTime(Number.parseFloat(event.target.value)) - - render() { - const {currentTime = 0, duration, startTime, endTime, hover, src} = this.props; - - if (!src) { - return null; - } - - const {hoverTime, resizing} = this.state; - - const total = endTime - startTime; - const current = currentTime - startTime; - - const previewTime = resizing ? currentTime : hoverTime; - const previewLabelTime = resizing ? currentTime : (startTime <= hoverTime && hoverTime <= endTime ? hoverTime - startTime : hoverTime); - const previewDuration = resizing ? total : (startTime <= hoverTime && hoverTime <= endTime ? total : undefined); - - const className = classNames('progress-bar-container', {hover}); - - return ( -
-
-
- -
- -
- - -
-
- -
- ); - } -} - -PlayBar.propTypes = { - startTime: PropTypes.number, - endTime: PropTypes.number, - seek: PropTypes.elementType, - currentTime: PropTypes.number, - duration: PropTypes.number, - src: PropTypes.string, - setStartTime: PropTypes.elementType, - setEndTime: PropTypes.elementType, - pause: PropTypes.elementType, - play: PropTypes.elementType, - hover: PropTypes.bool -}; - -export default connect( - [VideoContainer], - ({currentTime, duration, startTime, endTime, src}) => ({currentTime, duration, startTime, endTime, src}), - ({seek, setStartTime, setEndTime, pause, play}) => ({seek, setStartTime, setEndTime, pause, play}) -)(PlayBar); diff --git a/renderer/components/editor/controls/play-bar.tsx b/renderer/components/editor/controls/play-bar.tsx new file mode 100644 index 000000000..7e3d21585 --- /dev/null +++ b/renderer/components/editor/controls/play-bar.tsx @@ -0,0 +1,440 @@ +import VideoTimeContainer from '../video-time-container'; +import {useState, useRef} from 'react'; +import VideoControlsContainer from '../video-controls-container'; +import Preview from './preview'; + +const PlayBar = () => { + const [resizing, setResizing] = useState(false); + const [hoverTime, setHoverTime] = useState(0); + const progress = useRef(); + + const {play, pause} = VideoControlsContainer.useContainer(); + const { + currentTime, + duration, + startTime, + endTime, + updateTime, + updateStartTime, + updateEndTime + } = VideoTimeContainer.useContainer(); + + const total = endTime - startTime; + const current = currentTime - startTime; + + const getTimeFromEvent = event => { + const cursorX = event.clientX; + const {x, width} = progress.current.getBoundingClientRect(); + + const percent = (cursorX - x) / width; + const time = startTime + ((endTime - startTime) * percent); + + return Math.max(0, time); + }; + + const seek = event => { + const time = getTimeFromEvent(event); + + if (startTime <= time && time <= endTime) { + updateTime(time); + } + }; + + const updatePreview = event => { + setHoverTime(getTimeFromEvent(event)); + }; + + const startResizing = () => { + setResizing(true); + pause(); + }; + + const stopResizing = () => { + setResizing(false); + play(); + }; + + const setStartTime = event => { + updateStartTime(Number.parseFloat(event.target.value)); + }; + + const setEndTime = event => { + updateEndTime(Number.parseFloat(event.target.value)); + }; + + const previewTime = resizing ? currentTime : hoverTime; + const previewLabelTime = resizing ? currentTime : (startTime <= hoverTime && hoverTime <= endTime ? hoverTime - startTime : hoverTime); + const previewDuration = resizing ? total : (startTime <= hoverTime && hoverTime <= endTime ? total : undefined); + + return ( +
+
+
+ +
+ +
+ + +
+
+ +
+ ); +}; + +export default PlayBar; + +// Import PropTypes from 'prop-types'; +// import React from 'react'; +// import classNames from 'classnames'; + +// import {connect, VideoContainer} from '../../../containers'; +// import Preview from './preview'; + +// class PlayBar extends React.Component { +// state = { +// hoverTime: 0 +// }; + +// progress = React.createRef(); + +// getTimeFromEvent = event => { +// const {startTime, endTime} = this.props; + +// const cursorX = event.clientX; +// const {x, width} = this.progress.current.getBoundingClientRect(); + +// const percent = (cursorX - x) / width; +// const time = startTime + ((endTime - startTime) * percent); + +// return Math.max(0, time); +// } + +// seek = event => { +// const {startTime, endTime, seek} = this.props; +// const time = this.getTimeFromEvent(event); + +// if (startTime <= time && time <= endTime) { +// seek(time); +// } +// } + +// updatePreview = event => { +// const time = this.getTimeFromEvent(event); +// this.setState({hoverTime: time}); +// } + +// startResizing = () => { +// const {pause} = this.props; +// this.setState({resizing: true}); +// pause(); +// } + +// stopResizing = () => { +// const {play} = this.props; +// this.setState({resizing: false}); +// play(); +// } + +// setStartTime = event => this.props.setStartTime(Number.parseFloat(event.target.value)) + +// setEndTime = event => this.props.setEndTime(Number.parseFloat(event.target.value)) + +// render() { +// const {currentTime = 0, duration, startTime, endTime, hover, src} = this.props; + +// if (!src) { +// return null; +// } + +// const {hoverTime, resizing} = this.state; + +// const total = endTime - startTime; +// const current = currentTime - startTime; + +// const previewTime = resizing ? currentTime : hoverTime; +// const previewLabelTime = resizing ? currentTime : (startTime <= hoverTime && hoverTime <= endTime ? hoverTime - startTime : hoverTime); +// const previewDuration = resizing ? total : (startTime <= hoverTime && hoverTime <= endTime ? total : undefined); + +// const className = classNames('progress-bar-container', {hover}); + +// return ( +//
+//
+//
+// +//
+// +//
+// +// +//
+//
+// +//
+// ); +// } +// } + +// PlayBar.propTypes = { +// startTime: PropTypes.number, +// endTime: PropTypes.number, +// seek: PropTypes.elementType, +// currentTime: PropTypes.number, +// duration: PropTypes.number, +// src: PropTypes.string, +// setStartTime: PropTypes.elementType, +// setEndTime: PropTypes.elementType, +// pause: PropTypes.elementType, +// play: PropTypes.elementType, +// hover: PropTypes.bool +// }; + +// export default connect( +// [VideoContainer], +// ({currentTime, duration, startTime, endTime, src}) => ({currentTime, duration, startTime, endTime, src}), +// ({seek, setStartTime, setEndTime, pause, play}) => ({seek, setStartTime, setEndTime, pause, play}) +// )(PlayBar); diff --git a/renderer/components/editor/controls/preview.js b/renderer/components/editor/controls/preview.js deleted file mode 100644 index 9e2f390b7..000000000 --- a/renderer/components/editor/controls/preview.js +++ /dev/null @@ -1,73 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import formatTime from '../../../utils/format-time'; - -class Preview extends React.Component { - constructor(props) { - super(props); - this.videoRef = React.createRef(); - } - - shouldComponentUpdate(nextProps) { - return nextProps.time !== this.props.time || nextProps.hidePreview !== this.props.hidePreview; - } - - componentDidUpdate(previousProps) { - if (previousProps.time !== this.props.time) { - this.videoRef.current.currentTime = this.props.time; - } - } - - render() { - const {labelTime, duration, hidePreview, src} = this.props; - - return ( -
event.stopPropagation()}> -
- ); - } -} - -Preview.propTypes = { - time: PropTypes.number, - labelTime: PropTypes.number, - duration: PropTypes.number, - hidePreview: PropTypes.bool, - src: PropTypes.string -}; - -export default Preview; diff --git a/renderer/components/editor/controls/preview.tsx b/renderer/components/editor/controls/preview.tsx new file mode 100644 index 000000000..90e927a31 --- /dev/null +++ b/renderer/components/editor/controls/preview.tsx @@ -0,0 +1,140 @@ +import formatTime from '../../../utils/format-time'; +import {useRef, useEffect} from 'react'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; + +type Props = { + time: number; + labelTime: number; + duration: number; + hidePreview: boolean; +}; + +const Preview = ({time, labelTime, duration, hidePreview}: Props) => { + const videoRef = useRef(); + const {filePath} = useEditorWindowState(); + const src = `file://${filePath}`; + + useEffect(() => { + if (!hidePreview) { + videoRef.current.currentTime = time; + } + }, [time, hidePreview]); + + return ( +
{ + event.stopPropagation(); + }} + > +
+ ); +}; + +export default Preview; + +// Import PropTypes from 'prop-types'; +// import React from 'react'; + +// import formatTime from '../../../utils/format-time'; + +// class Preview extends React.Component { +// constructor(props) { +// super(props); +// this.videoRef = React.createRef(); +// } + +// shouldComponentUpdate(nextProps) { +// return nextProps.time !== this.props.time || nextProps.hidePreview !== this.props.hidePreview; +// } + +// componentDidUpdate(previousProps) { +// if (previousProps.time !== this.props.time) { +// this.videoRef.current.currentTime = this.props.time; +// } +// } + +// render() { +// const {labelTime, duration, hidePreview, src} = this.props; + +// return ( +//
event.stopPropagation()}> +//
+// ); +// } +// } + +// Preview.propTypes = { +// time: PropTypes.number, +// labelTime: PropTypes.number, +// duration: PropTypes.number, +// hidePreview: PropTypes.bool, +// src: PropTypes.string +// }; + +// export default Preview; diff --git a/renderer/components/editor/controls/right.js b/renderer/components/editor/controls/right.js deleted file mode 100644 index 4a8814da7..000000000 --- a/renderer/components/editor/controls/right.js +++ /dev/null @@ -1,65 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import {VolumeHighIcon, VolumeOffIcon} from '../../../vectors'; -import {connect, VideoContainer, EditorContainer} from '../../../containers'; - -import formatTime from '../../../utils/format-time'; - -class RightControls extends React.Component { - render() { - const {isMuted, mute, unmute, format, duration, hasAudio} = this.props; - const canUnmute = !['gif', 'apng'].includes(format) && hasAudio; - const unmuteColor = canUnmute ? '#fff' : 'rgba(255, 255, 255, 0.40)'; - return ( -
-
{formatTime(duration)}
-
- { - isMuted || !hasAudio ? - : - - } -
- -
- ); - } -} - -RightControls.propTypes = { - hasAudio: PropTypes.bool, - isMuted: PropTypes.bool, - mute: PropTypes.elementType, - unmute: PropTypes.elementType, - format: PropTypes.string, - duration: PropTypes.number -}; - -export default connect( - [VideoContainer, EditorContainer], - ({isMuted, duration, hasAudio}, {format}) => ({hasAudio, isMuted, format, duration}), - ({mute, unmute}) => ({mute, unmute}) -)(RightControls); diff --git a/renderer/components/editor/controls/right.tsx b/renderer/components/editor/controls/right.tsx new file mode 100644 index 000000000..101b52712 --- /dev/null +++ b/renderer/components/editor/controls/right.tsx @@ -0,0 +1,54 @@ +import {VolumeHighIcon, VolumeOffIcon} from '../../../vectors'; +import VideoControlsContainer from '../video-controls-container'; +import VideoMetadataContainer from '../video-metadata-container'; + +import formatTime from '../../../utils/format-time'; + +const RightControls = () => { + const {isMuted, mute, unmute} = VideoControlsContainer.useContainer(); + const {hasAudio, duration} = VideoMetadataContainer.useContainer(); + + // FIXME + const format = 'mp4'; + + const canUnmute = !['gif', 'apng'].includes(format) && hasAudio; + const unmuteColor = canUnmute ? '#fff' : 'rgba(255, 255, 255, 0.40)'; + + return ( +
+
{formatTime(duration)}
+
+ { + isMuted || !hasAudio ? + : + + } +
+ +
+ ); +}; + +export default RightControls; diff --git a/renderer/components/editor/conversion/conversion-details.tsx b/renderer/components/editor/conversion/conversion-details.tsx new file mode 100644 index 000000000..947c63fae --- /dev/null +++ b/renderer/components/editor/conversion/conversion-details.tsx @@ -0,0 +1,74 @@ +import {UseConversionState} from 'hooks/editor/use-conversion'; + +const ConversionDetails = ({conversion}: {conversion: UseConversionState}) => { + const message = conversion?.message ?? 'Loading…'; + const title = conversion?.title; + const description = conversion?.description; + const size = conversion?.size; + + return ( +
+
{message}
+
+
+
{title}
+
{description}
+
+
{size}
+
+ +
+ ); +}; + +export default ConversionDetails; diff --git a/renderer/components/editor/conversion/index.tsx b/renderer/components/editor/conversion/index.tsx new file mode 100644 index 000000000..f5c10b959 --- /dev/null +++ b/renderer/components/editor/conversion/index.tsx @@ -0,0 +1,58 @@ +import {ConversionStatus} from 'common/types'; +import useConversion from 'hooks/editor/use-conversion'; +import useConversionIdContext from 'hooks/editor/use-conversion-id'; +import {useConfirmation} from 'hooks/use-confirmation'; +import {useMemo} from 'react'; +import {useKeyboardAction} from '../../../hooks/use-keyboard-action'; +import ConversionDetails from './conversion-details'; +import TitleBar from './title-bar'; +import VideoPreview from './video-preview'; + +const dialogOptions = { + message: 'Are you sure you want to discard this conversion?', + detail: 'Any progress will be lost.', + confirmButtonText: 'Discard' +}; + +const EditorConversionView = ({conversionId}: {conversionId: string}) => { + const {setConversionId} = useConversionIdContext(); + const conversion = useConversion(conversionId); + + const cancel = () => { + conversion.cancel(); + }; + + const safeCancel = useConfirmation(cancel, dialogOptions); + + const cancelAndGoBack = () => { + cancel(); + setConversionId(''); + }; + + const inProgress = conversion.state?.status === ConversionStatus.inProgress; + + const finalCancel = useMemo(() => inProgress ? safeCancel : () => { /* do nothing */ }, [inProgress]); + + useKeyboardAction('Escape', finalCancel); + + return ( +
+ { + conversion.copy(); + }}/> + + + +
+ ); +}; + +export default EditorConversionView; diff --git a/renderer/components/editor/conversion/title-bar.tsx b/renderer/components/editor/conversion/title-bar.tsx new file mode 100644 index 000000000..7083cdb05 --- /dev/null +++ b/renderer/components/editor/conversion/title-bar.tsx @@ -0,0 +1,87 @@ +import TrafficLights from 'components/traffic-lights'; +import {BackPlainIcon} from 'vectors'; +import {UseConversionState} from 'hooks/editor/use-conversion'; +import {flags} from '../../../common/flags'; +import {remote} from 'electron'; +import {ConversionStatus} from '../../../common/types'; + +const TitleBar = ({conversion, cancel, copy}: {conversion: UseConversionState; cancel: () => any; copy: () => any}) => { + const {api} = require('electron-util'); + const shouldClose = async () => { + if (conversion.status === ConversionStatus.inProgress && !flags.get('backgroundEditorConversion')) { + await api.dialog.showMessageBox(remote.getCurrentWindow(), { + type: 'info', + message: 'Your export will continue in the background. You can access it through the Export History window.', + buttons: ['Ok'], + defaultId: 0 + }); + flags.set('backgroundEditorConversion', true); + } + + return true; + }; + + return ( +
+
+ +
+ +
+
+
+ {conversion?.canCopy &&
Copy
} +
+ +
+ ); +}; + +export default TitleBar; diff --git a/renderer/components/editor/conversion/video-preview.tsx b/renderer/components/editor/conversion/video-preview.tsx new file mode 100644 index 000000000..20e977fbf --- /dev/null +++ b/renderer/components/editor/conversion/video-preview.tsx @@ -0,0 +1,144 @@ +import {CancelIcon, SpinnerIcon} from 'vectors'; +import {UseConversion, UseConversionState} from 'hooks/editor/use-conversion'; +import {ConversionStatus} from 'common/types'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; +import useConversionIdContext from 'hooks/editor/use-conversion-id'; + +const VideoPreview = ({conversion, cancel}: {conversion: UseConversionState; cancel: () => any}) => { + const {conversionId} = useConversionIdContext(); + const {filePath} = useEditorWindowState(); + const src = `file://${filePath}`; + + const percentage = conversion?.progress ?? 0; + const done = conversion?.status !== ConversionStatus.inProgress; + + const onDragStart = (event: any) => { + event.preventDefault(); + // Has to be the electron one for this + const {ipcRenderer} = require('electron'); + ipcRenderer.send('drag-conversion', conversionId); + }; + + return ( +
+
+ ); +}; + +const IndeterminateSpinner = () => ( +
+ + +
+); + +const ProgressCircle = ({percent}: {percent: number}) => { + const circumference = 12 * 2 * Math.PI; + const offset = circumference * (1 - percent); + + return ( + + + + + ); +}; + +export default VideoPreview; diff --git a/renderer/components/editor/editor-preview.tsx b/renderer/components/editor/editor-preview.tsx new file mode 100644 index 000000000..ca70beeef --- /dev/null +++ b/renderer/components/editor/editor-preview.tsx @@ -0,0 +1,73 @@ +import TrafficLights from '../traffic-lights'; +import VideoPlayer from './video-player'; +import Options from './options'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; + +const EditorPreview = () => { + const {title = 'Editor'} = useEditorWindowState(); + + return ( +
+
+
+
+ +
{title}
+
+
+ +
+ + +
+ ); +}; + +export default EditorPreview; diff --git a/renderer/components/editor/index.js b/renderer/components/editor/index.js deleted file mode 100644 index 9149c70ae..000000000 --- a/renderer/components/editor/index.js +++ /dev/null @@ -1,88 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; - -import {connect, EditorContainer} from '../../containers'; -import TrafficLights from '../traffic-lights'; -import VideoPlayer from './video-player'; - -class Editor extends React.Component { - state = { - hover: false - } - - mouseEnter = () => { - this.setState({hover: true}); - } - - mouseLeave = () => { - this.setState({hover: false}); - } - - render() { - const {hover} = this.state; - const {title = 'Editor'} = this.props; - - return ( -
-
-
- -
{title}
-
-
- - -
- ); - } -} - -Editor.propTypes = { - title: PropTypes.string -}; - -export default connect( - [EditorContainer], - ({title}) => ({title}) -)(Editor); diff --git a/renderer/components/editor/index.tsx b/renderer/components/editor/index.tsx new file mode 100644 index 000000000..e73f4ceb4 --- /dev/null +++ b/renderer/components/editor/index.tsx @@ -0,0 +1,59 @@ +import useConversionIdContext from 'hooks/editor/use-conversion-id'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; +import {useEditorWindowSizeEffect} from 'hooks/editor/use-window-size'; +import {useEffect, useState} from 'react'; +import EditorConversionView from './conversion'; +import EditorPreview from './editor-preview'; +import classNames from 'classnames'; + +const Editor = () => { + const {conversionId, setConversionId} = useConversionIdContext(); + const state = useEditorWindowState(); + const [isConversionPreviewState, setIsConversionPreviewState] = useState(false); + + useEffect(() => { + if (state.conversionId && !conversionId) { + setConversionId(state.conversionId); + } + }, [state.conversionId]); + + useEditorWindowSizeEffect(isConversionPreviewState); + + const isTransitioning = Boolean(conversionId) !== isConversionPreviewState; + + const className = classNames('container', { + transitioning: isTransitioning + }); + + const onTransitionEnd = () => { + setIsConversionPreviewState(Boolean(conversionId)); + }; + + return ( +
+ { + isConversionPreviewState ? + : + + } + +
+ ); +}; + +export default Editor; diff --git a/renderer/components/editor/options-container.tsx b/renderer/components/editor/options-container.tsx new file mode 100644 index 000000000..c39b435f4 --- /dev/null +++ b/renderer/components/editor/options-container.tsx @@ -0,0 +1,151 @@ +import {useState, useEffect, useMemo} from 'react'; +import {createContainer} from 'unstated-next'; +import {debounce, DebouncedFunc} from 'lodash'; + +import VideoMetadataContainer from './video-metadata-container'; +import VideoControlsContainer from './video-controls-container'; +import useEditorOptions, {EditorOptionsState} from 'hooks/editor/use-editor-options'; +import {Format, App} from 'common/types'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; + +type EditService = EditorOptionsState['editServices'][0]; + +type SharePlugin = { + pluginName: string; + serviceTitle: string; + app?: App; +}; + +const isFormatMuted = (format: Format) => ['gif', 'apng'].includes(format); + +const useOptions = () => { + const {fps: originalFps} = useEditorWindowState(); + const { + state: { + formats, + fpsHistory, + editServices + }, + updateFpsUsage, + isLoading + } = useEditorOptions(); + + const metadata = VideoMetadataContainer.useContainer(); + const {isMuted, mute, unmute} = VideoControlsContainer.useContainer(); + + const [format, setFormat] = useState(); + const [fps, setFps] = useState(); + const [width, setWidth] = useState(); + const [height, setHeight] = useState(); + const [editPlugin, setEditPlugin] = useState(); + const [sharePlugin, setSharePlugin] = useState(); + + const [wasMuted, setWasMuted] = useState(false); + + const debouncedUpdateFpsUsage = useMemo(() => { + if (!updateFpsUsage) { + return; + } + + return debounce(updateFpsUsage, 1000); + }, [updateFpsUsage]); + + const updateFps = (newFps: number, formatName = format) => { + setFps(newFps); + debouncedUpdateFpsUsage?.({format: formatName, fps: newFps}); + }; + + const updateSharePlugin = (plugin: SharePlugin) => { + setSharePlugin(plugin); + }; + + const updateFormat = (formatName: Format) => { + debouncedUpdateFpsUsage.flush(); + + if (metadata.hasAudio) { + if (isFormatMuted(formatName) && !isFormatMuted(format)) { + setWasMuted(isMuted); + mute(); + } else if (!isFormatMuted(formatName) && isFormatMuted(format) && !wasMuted) { + unmute(); + } + } + + const formatOption = formats.find(f => f.format === formatName); + const selectedSharePlugin = formatOption.plugins.find(plugin => { + return ( + plugin.pluginName === sharePlugin.pluginName && + plugin.title === sharePlugin.serviceTitle && + (plugin.apps?.some(app => app.url === sharePlugin.app?.url) ?? true) + ); + }) ?? formatOption.plugins.find(plugin => plugin.pluginName !== '_openWith'); + + setFormat(formatName); + setSharePlugin({ + pluginName: selectedSharePlugin.pluginName, + serviceTitle: selectedSharePlugin.title, + app: selectedSharePlugin.apps ? sharePlugin.app : undefined + }); + updateFps(Math.min(originalFps, fpsHistory[formatName]), formatName); + }; + + useEffect(() => { + if (isLoading) { + return; + } + + const firstFormat = formats[0]; + const formatName = firstFormat.format; + + setFormat(formatName); + + const firstPlugin = firstFormat.plugins.find(plugin => plugin.pluginName !== '_openWith'); + + setSharePlugin(firstPlugin && { + pluginName: firstPlugin.pluginName, + serviceTitle: firstPlugin.title + }); + + updateFps(Math.min(originalFps, fpsHistory[formatName]), formatName); + }, [isLoading]); + + useEffect(() => { + setWidth(metadata.width); + setHeight(metadata.height); + }, [metadata]); + + useEffect(() => { + if (!editPlugin) { + return; + } + + const newPlugin = editServices.find(service => service.pluginName === editPlugin.pluginName && service.title === editPlugin.title); + setEditPlugin(newPlugin); + }, [editServices]); + + const setDimensions = (dimensions: {width: number; height: number}) => { + setWidth(dimensions.width); + setHeight(dimensions.height); + }; + + return { + width, + height, + format, + fps, + originalFps, + editPlugin, + formats, + editServices, + sharePlugin, + updateSharePlugin, + updateFps, + updateFormat, + setEditPlugin, + setDimensions + }; +}; + +const OptionsContainer = createContainer(useOptions); + +export default OptionsContainer; diff --git a/renderer/components/editor/options/index.js b/renderer/components/editor/options/index.tsx similarity index 55% rename from renderer/components/editor/options/index.js rename to renderer/components/editor/options/index.tsx index 63ac1b568..4f848b06b 100644 --- a/renderer/components/editor/options/index.js +++ b/renderer/components/editor/options/index.tsx @@ -1,15 +1,12 @@ -import React from 'react'; - import LeftOptions from './left'; import RightOptions from './right'; -export default class Options extends React.Component { - render() { - return ( -
- - - -
- ); - } -} +
+ ); +}; + +export default Options; diff --git a/renderer/components/editor/options/left.js b/renderer/components/editor/options/left.js deleted file mode 100644 index 717306b4c..000000000 --- a/renderer/components/editor/options/left.js +++ /dev/null @@ -1,137 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import css from 'styled-jsx/css'; -import {connect, EditorContainer} from '../../../containers'; -import KeyboardNumberInput from '../../keyboard-number-input'; -import Slider from './slider'; - -const {className: keyboardInputClass, styles: keyboardInputStyles} = css.resolve` - height: 24px; - background: rgba(255, 255, 255, 0.1); - text-align: center; - font-size: 12px; - box-sizing: border-box; - border: none; - padding: 4px; - border-bottom-left-radius: 4px; - border-top-left-radius: 4px; - width: 48px; - color: white; - box-shadow: inset 0px 1px 0px 0px rgba(255, 255, 255, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.2); - - input + input { - border-bottom-left-radius: 0; - border-top-left-radius: 0; - border-bottom-right-radius: 4px; - border-top-right-radius: 4px; - margin-left: 1px; - margin-right: 16px; - } - - :focus, :hover { - outline: none; - background: hsla(0, 0%, 100%, 0.2); - } -`; - -class LeftOptions extends React.Component { - handleBlur = event => { - const {changeDimension} = this.props; - changeDimension(event, {ignoreEmpty: false}); - } - - render() { - const {width, height, changeDimension, fps, originalFps, setFps, original} = this.props; - - return ( -
-
Size
- - -
FPS
-
- -
- {keyboardInputStyles} - -
- ); - } -} - -LeftOptions.propTypes = { - width: PropTypes.number, - height: PropTypes.number, - changeDimension: PropTypes.elementType, - fps: PropTypes.number, - setFps: PropTypes.elementType, - originalFps: PropTypes.number, - original: PropTypes.shape({ - width: PropTypes.number, - height: PropTypes.number - }) -}; - -export default connect( - [EditorContainer], - ({width, height, fps, originalFps, original}) => ({width, height, fps, originalFps, original}), - ({changeDimension, setFps}) => ({changeDimension, setFps}) -)(LeftOptions); diff --git a/renderer/components/editor/options/left.tsx b/renderer/components/editor/options/left.tsx new file mode 100644 index 000000000..f3583ce3d --- /dev/null +++ b/renderer/components/editor/options/left.tsx @@ -0,0 +1,385 @@ +import css from 'styled-jsx/css'; +import KeyboardNumberInput from '../../keyboard-number-input'; +import Slider from './slider'; +import OptionsContainer from '../options-container'; +import {useState, useEffect, useMemo} from 'react'; +import * as stringMath from 'string-math'; +import VideoMetadataContainer from '../video-metadata-container'; +import {shake} from '../../../utils/inputs'; +import Select, {Separator} from './select'; + +const percentValues = [100, 75, 50, 33, 25, 20, 10]; + +const {className: keyboardInputClass, styles: keyboardInputStyles} = css.resolve` + height: 24px; + background: rgba(255, 255, 255, 0.1); + text-align: center; + font-size: 12px; + box-sizing: border-box; + border: none; + padding: 4px; + border-bottom-left-radius: 4px; + border-top-left-radius: 4px; + width: 48px; + color: white; + box-shadow: inset 0px 1px 0px 0px rgba(255, 255, 255, 0.04), 0px 1px 2px 0px rgba(0, 0, 0, 0.2); + + input + input { + border-bottom-left-radius: 0; + border-top-left-radius: 0; + border-bottom-right-radius: 4px; + border-top-right-radius: 4px; + margin-left: 1px; + margin-right: 16px; + } + + :focus, :hover { + outline: none; + background: hsla(0, 0%, 100%, 0.2); + } +`; + +const LeftOptions = () => { + const {width, height, setDimensions, fps, updateFps, originalFps} = OptionsContainer.useContainer(); + const metadata = VideoMetadataContainer.useContainer(); + + const [widthValue, setWidthValue] = useState(); + const [heightValue, setHeightValue] = useState(); + + const onChange = (event, {ignoreEmpty = true}: {ignoreEmpty?: boolean} = {}) => { + if (!ignoreEmpty) { + onBlur(event); + return; + } + + const {currentTarget: {name, value}} = event; + if (name === 'width') { + setWidthValue(value); + } else { + setHeightValue(value); + } + }; + + const onBlur = event => { + const {currentTarget} = event; + const {name} = currentTarget; + + let value: number; + try { + value = stringMath(currentTarget.value); + } catch {} + + // Fallback to last valid + const updates = {width, height}; + + if (value) { + value = Math.round(value); + const ratio = metadata.width / metadata.height; + + if (name === 'width') { + const min = Math.max(1, Math.ceil(ratio)); + + if (value < min) { + shake(currentTarget, {className: 'shake-left'}); + updates.width = min; + } else if (value > metadata.width) { + shake(currentTarget, {className: 'shake-left'}); + updates.width = metadata.width; + } else { + updates.width = value; + } + + updates.height = Math[ratio > 1 ? 'ceil' : 'floor'](updates.width / ratio); + } else { + const min = Math.max(1, Math.ceil(1 / ratio)); + + if (value < min) { + shake(currentTarget, {className: 'shake-right'}); + updates.height = min; + } else if (value > metadata.height) { + shake(currentTarget, {className: 'shake-right'}); + updates.height = metadata.height; + } else { + updates.height = value; + } + + updates.width = Math[ratio > 1 ? 'floor' : 'ceil'](updates.height * ratio); + } + } else if (name === 'width') { + shake(currentTarget, {className: 'shake-left'}); + } else { + shake(currentTarget, {className: 'shake-right'}); + } + + setDimensions(updates); + setWidthValue(updates.width.toString()); + setHeightValue(updates.height.toString()); + }; + + useEffect(() => { + if (width && height) { + setWidthValue(width.toString()); + setHeightValue(height.toString()); + } + }, [width, height]); + + const percentOptions = useMemo(() => { + const ratio = metadata.width / metadata.height; + + const options = percentValues.map(percent => { + const adjustedWidth = Math.round(metadata.width * (percent / 100)); + const adjustedHeight = Math[ratio > 1 ? 'ceil' : 'floor'](adjustedWidth / ratio); + + return { + label: `${adjustedWidth} x ${adjustedHeight} (${percent === 100 ? 'Original' : `${percent}%`})`, + value: {width: adjustedWidth, height: adjustedHeight}, + checked: width === adjustedWidth + }; + }); + + if (options.every(opt => !opt.checked)) { + return [ + { + label: 'Custom', + value: {width, height}, + checked: true + }, + { + separator: true + }, + ...options + ]; + } + + return options; + }, [metadata, width, height]); + + const selectPercentage = updates => { + setDimensions(updates); + setWidthValue(updates.width.toString()); + setHeightValue(updates.height.toString()); + }; + + const percentLabel = `${Math.round((width / metadata.width) * 100)}%`; + + return ( +
+
Size
+ + +
+ -
- ) - } - - ) - } -
- -
- - -
- ); - } -} - -RightOptions.propTypes = { - options: PropTypes.arrayOf(PropTypes.object), - format: PropTypes.string, - plugin: PropTypes.string, - selectFormat: PropTypes.elementType, - selectPlugin: PropTypes.elementType, - startExport: PropTypes.elementType, - openWithApp: PropTypes.object, - selectOpenWithApp: PropTypes.elementType, - editPlugin: PropTypes.object, - editOptions: PropTypes.arrayOf(PropTypes.object), - selectEditPlugin: PropTypes.elementType, - openEditPluginConfig: PropTypes.elementType -}; - -export default connect( - [EditorContainer], - ({options, format, plugin, openWithApp, editOptions, editPlugin}) => ({options, format, plugin, openWithApp, editOptions, editPlugin}), - ({selectFormat, selectPlugin, startExport, selectOpenWithApp, selectEditPlugin, openEditPluginConfig}) => ({selectFormat, selectPlugin, startExport, selectOpenWithApp, selectEditPlugin, openEditPluginConfig}) -)(RightOptions); diff --git a/renderer/components/editor/options/right.tsx b/renderer/components/editor/options/right.tsx new file mode 100644 index 000000000..50a2abb6b --- /dev/null +++ b/renderer/components/editor/options/right.tsx @@ -0,0 +1,411 @@ +import {GearIcon} from '../../../vectors'; +import OptionsContainer from '../options-container'; +import Select from './select'; +import {ipcRenderer as ipc} from 'electron-better-ipc'; +import useConversionIdContext from 'hooks/editor/use-conversion-id'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; +import VideoTimeContainer from '../video-time-container'; +import VideoControlsContainer from '../video-controls-container'; +import useSharePlugins from 'hooks/editor/use-share-plugins'; +import useEditorOptions from 'hooks/editor/use-editor-options'; + +const FormatSelect = () => { + const {formats, format, updateFormat} = OptionsContainer.useContainer(); + const options = formats.map(format => ({label: format.prettyFormat, value: format.format})); + + return ; +}; + +const EditPluginsControl = () => { + const {editServices, editPlugin, setEditPlugin} = OptionsContainer.useContainer(); + + if (editServices?.length === 0) { + return null; + } + + if (!editPlugin) { + return ( + + ); + } + + const openEditPluginConfig = () => { + ipc.callMain('open-edit-config', { + pluginName: editPlugin.pluginName, + serviceTitle: editPlugin.title + }); + }; + + const options = editServices.map(service => ({label: service.title, value: service})); + + return ( + <> + { + editPlugin.hasConfig && ( + + ) + } +
+ +//
+// ) +// } +// +// ) +// } +//
+// +//
+// +// +// +// ); +// } +// } + +// RightOptions.propTypes = { +// options: PropTypes.arrayOf(PropTypes.object), +// format: PropTypes.string, +// plugin: PropTypes.string, +// selectFormat: PropTypes.elementType, +// selectPlugin: PropTypes.elementType, +// startExport: PropTypes.elementType, +// openWithApp: PropTypes.object, +// selectOpenWithApp: PropTypes.elementType, +// editPlugin: PropTypes.object, +// editOptions: PropTypes.arrayOf(PropTypes.object), +// selectEditPlugin: PropTypes.elementType, +// openEditPluginConfig: PropTypes.elementType +// }; + +// export default connect( +// [EditorContainer], +// ({options, format, plugin, openWithApp, editOptions, editPlugin}) => ({options, format, plugin, openWithApp, editOptions, editPlugin}), +// ({selectFormat, selectPlugin, startExport, selectOpenWithApp, selectEditPlugin, openEditPluginConfig}) => ({selectFormat, selectPlugin, startExport, selectOpenWithApp, selectEditPlugin, openEditPluginConfig}) +// )(RightOptions); diff --git a/renderer/components/editor/options/select.js b/renderer/components/editor/options/select.js deleted file mode 100644 index aaa7bdc25..000000000 --- a/renderer/components/editor/options/select.js +++ /dev/null @@ -1,119 +0,0 @@ -import electron from 'electron'; -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import {DropdownArrowIcon, CancelIcon} from '../../../vectors'; - -class Select extends React.Component { - select = React.createRef(); - - handleClick = () => { - const {options, onChange, selected} = this.props; - if (options.length > 0) { - const boundingRect = this.select.current.getBoundingClientRect(); - - const {Menu} = electron.remote; - const menu = Menu.buildFromTemplate(options.map(option => { - if (option.submenu) { - return { - label: option.value, - submenu: option.submenu - }; - } - - if (option.separator) { - return {type: 'separator'}; - } - - return { - label: option.label, - type: option.type || 'radio', - checked: option.value === selected, - click: () => onChange(option.value) - }; - })); - - menu.popup({ - x: Math.round(boundingRect.left), - y: Math.round(boundingRect.top) - }); - } - } - - handleDropdownClick = event => { - const {clearable, selected, onChange} = this.props; - - if (clearable && selected) { - event.stopPropagation(); - onChange(); - } - } - - render() { - const {options = [], selected, clearable} = this.props; - const selectedOption = options.find(opt => opt.value === selected); - const label = selectedOption && selectedOption.label; - - return ( -
-
{label}
-
- { - clearable && selected ? - : - - } -
- -
- ); - } -} - -Select.propTypes = { - clearable: PropTypes.bool, - onChange: PropTypes.elementType, - options: PropTypes.arrayOf(PropTypes.object), - selected: PropTypes.any -}; - -export default Select; diff --git a/renderer/components/editor/options/select.tsx b/renderer/components/editor/options/select.tsx new file mode 100644 index 000000000..d78d75446 --- /dev/null +++ b/renderer/components/editor/options/select.tsx @@ -0,0 +1,145 @@ +import {DropdownArrowIcon, CancelIcon} from '../../../vectors'; +import classNames from 'classnames'; +import {useRef} from 'react'; +import {remote, MenuItemConstructorOptions, NativeImage} from 'electron'; + +type Option = { + label: string; + value: T; + subMenu?: Array>; + type?: string; + checked?: boolean; + click?: () => void; + separator?: false; + icon?: NativeImage; +}; + +export type Separator = { + value: never; + label: never; + subMenu: never; + type: never; + checked: never; + icon: never; + separator: true; +}; + +interface Props { + value?: T; + options: Array | Separator>; + onChange: (newValue?: T) => void; + clearable?: boolean; + customLabel?: string; +} + +// eslint-disable-next-line @typescript-eslint/comma-dangle +const Select = (props: Props) => { + const select = useRef(); + const {options = [], value} = props; + + const selectedOption = options.find(opt => opt.value === value); + const selectedLabel = props.customLabel ?? (selectedOption?.label); + const clearable = props.clearable && selectedOption; + + const handleClick = () => { + if (options.length === 0) { + return; + } + + const boundingRect = select.current.getBoundingClientRect(); + + const {Menu} = remote; + + const convertToMenuTemplate = (option: Option | Separator): MenuItemConstructorOptions => { + if (option.separator) { + return {type: 'separator'}; + } + + if (option.subMenu) { + return { + label: option.label, + submenu: option.subMenu.map(opt => convertToMenuTemplate(opt)), + checked: option.checked + }; + } + + return { + label: option.label, + type: option.type as any || 'checkbox', + checked: option.checked ?? (option.value === value), + click: option.click ?? (() => { + props.onChange(option.value); + }), + icon: option.icon + }; + }; + + const menu = Menu.buildFromTemplate(options.map(opt => convertToMenuTemplate(opt))); + + menu.popup({ + x: Math.round(boundingRect.left), + y: Math.round(boundingRect.top) + }); + }; + + const handleDropdownClick = event => { + if (clearable) { + event.stopPropagation(); + props.onChange(); + } + }; + + return ( +
+
{selectedLabel}
+
+ { + clearable ? + : + + } +
+ +
+ ); +}; + +export default Select; diff --git a/renderer/components/editor/options/slider.js b/renderer/components/editor/options/slider.js deleted file mode 100644 index 0b003fa57..000000000 --- a/renderer/components/editor/options/slider.js +++ /dev/null @@ -1,154 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import {TooltipIcon} from '../../../vectors'; - -class Slider extends React.Component { - state = { - isOpen: false - } - - show = () => this.setState({isOpen: true}) - - hide = () => this.setState({isOpen: false}) - - handleChange = event => { - const {onChange} = this.props; - onChange(event.target.value, event.target); - } - - handleBlur = event => { - const {onChange} = this.props; - onChange(event.target.value, event.target, {ignoreEmpty: false}); - } - - render() { - const {value, max, min} = this.props; - const {isOpen} = this.state; - - return ( -
- { isOpen &&
} - - { - isOpen && ( -
event.stopPropagation()}> - -
- -
-
- ) - } - -
- ); - } -} - -Slider.propTypes = { - value: PropTypes.number, - max: PropTypes.number, - min: PropTypes.number, - onChange: PropTypes.elementType -}; - -export default Slider; diff --git a/renderer/components/editor/options/slider.tsx b/renderer/components/editor/options/slider.tsx new file mode 100644 index 000000000..396a53862 --- /dev/null +++ b/renderer/components/editor/options/slider.tsx @@ -0,0 +1,352 @@ +import {TooltipIcon} from '../../../vectors'; +import {useState, useEffect} from 'react'; +import {shake} from '../../../utils/inputs'; + +interface Props { + value: number; + onChange: (newValue: number) => void; + min: number; + max: number; +} + +const Slider = (props: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [valueText, setValueText] = useState(props.value?.toString()); + + useEffect(() => { + setValueText(props.value?.toString()); + }, [props.value]); + + const onChange = event => { + setValueText(event.currentTarget.value); + }; + + const onBlur = event => { + const {currentTarget} = event; + const value = Number.parseInt(currentTarget.value, 10); + + if (value && value >= props.min && value <= props.max) { + props.onChange(value); + setValueText(value.toString()); + } else if (value) { + const newValue = Math.min(Math.max(value, props.min), props.max); + props.onChange(newValue); + setValueText(newValue.toString()); + shake(currentTarget); + } else { + setValueText(props.value.toString()); + shake(currentTarget); + } + }; + + const onKeyDown = event => { + if (event.key === 'Enter') { + onBlur(event); + } + }; + + const onSliderChange = event => { + const value = Number.parseInt(event.currentTarget.value, 10); + props.onChange(value); + setValueText(value.toString()); + }; + + return ( +
+ {isOpen &&
{ + setIsOpen(false); + }}/>} + { + setIsOpen(true); + }} + /> + { + isOpen && ( +
{ + event.stopPropagation(); + }} + > + { + setIsOpen(false); + }} + /> +
+ +
+
+ ) + } + +
+ ); +}; + +export default Slider; + +// Import React from 'react'; +// import PropTypes from 'prop-types'; + +// class Slider extends React.Component { +// state = { +// isOpen: false +// } + +// show = () => this.setState({isOpen: true}) + +// hide = () => this.setState({isOpen: false}) + +// handleChange = event => { +// const {onChange} = this.props; +// onChange(event.target.value, event.target); +// } + +// handleBlur = event => { +// const {onChange} = this.props; +// onChange(event.target.value, event.target, {ignoreEmpty: false}); +// } + +// render() { +// const {value, max, min} = this.props; +// const {isOpen} = this.state; + +// return ( +//
+// { isOpen &&
} +// +// { +// isOpen && ( +//
event.stopPropagation()}> +// +//
+// +//
+//
+// ) +// } +// +//
+// ); +// } +// } + +// Slider.propTypes = { +// value: PropTypes.number, +// max: PropTypes.number, +// min: PropTypes.number, +// onChange: PropTypes.elementType +// }; + +// export default Slider; diff --git a/renderer/components/editor/video-controls-container.tsx b/renderer/components/editor/video-controls-container.tsx new file mode 100644 index 000000000..5737bf40e --- /dev/null +++ b/renderer/components/editor/video-controls-container.tsx @@ -0,0 +1,114 @@ +import {createContainer} from 'unstated-next'; +import electron from 'electron'; +import {useRef, useState, useEffect} from 'react'; + +const useVideoControls = () => { + const videoRef = useRef(); + const currentWindow = electron.remote.getCurrentWindow(); + const wasPaused = useRef(true); + const transitioningPauseState = useRef>(); + + const [hasStarted, setHasStarted] = useState(false); + const [isMuted, setIsMuted] = useState(false); + const [isPaused, setIsPaused] = useState(true); + + const play = async () => { + if (videoRef.current?.paused) { + transitioningPauseState.current = videoRef.current.play(); + try { + await transitioningPauseState.current; + setIsPaused(false); + } catch {} + } + }; + + const pause = async () => { + if (videoRef.current && !videoRef.current.paused) { + try { + await transitioningPauseState.current; + } catch {} finally { + videoRef.current.pause(); + setIsPaused(true); + } + } + }; + + const mute = () => { + setIsMuted(true); + videoRef.current.muted = true; + }; + + const unmute = () => { + setIsMuted(false); + videoRef.current.muted = false; + }; + + const setVideoRef = (video: HTMLVideoElement) => { + videoRef.current = video; + setIsPaused(video.paused); + + if (video.paused) { + play(); + } + }; + + const videoProps = { + onCanPlayThrough: hasStarted ? undefined : () => { + setHasStarted(true); + if (currentWindow.isFocused()) { + play(); + } + }, + onLoadedData: () => { + const hasAudio = (videoRef.current as any).webkitAudioDecodedByteCount > 0 || Boolean( + (videoRef.current as any).audioTracks && + (videoRef.current as any).audioTracks.length > 0 + ); + + if (!hasAudio) { + mute(); + } + }, + onEnded: () => { + play(); + } + }; + + useEffect(() => { + const blurListener = () => { + wasPaused.current = videoRef.current?.paused; + if (!wasPaused.current) { + pause(); + } + }; + + const focusListener = () => { + if (!wasPaused.current) { + play(); + } + }; + + currentWindow.addListener('blur', blurListener); + currentWindow.addListener('focus', focusListener); + + return () => { + currentWindow.removeListener('blur', blurListener); + currentWindow.removeListener('focus', focusListener); + }; + }, []); + + return { + isPaused, + isMuted, + setVideoRef, + pause, + play, + mute, + unmute, + videoProps + }; +}; + +const VideoControlsContainer = createContainer(useVideoControls); + +export default VideoControlsContainer; diff --git a/renderer/components/editor/video-metadata-container.tsx b/renderer/components/editor/video-metadata-container.tsx new file mode 100644 index 000000000..f7775d048 --- /dev/null +++ b/renderer/components/editor/video-metadata-container.tsx @@ -0,0 +1,49 @@ +import {createContainer} from 'unstated-next'; +import {useRef, useState} from 'react'; + +const useVideoMetadata = () => { + const videoRef = useRef(); + + const [width, setWidth] = useState(0); + const [height, setHeight] = useState(0); + const [hasAudio, setHasAudio] = useState(false); + const [duration, setDuration] = useState(0); + + const setVideoRef = (video: HTMLVideoElement) => { + videoRef.current = video; + }; + + const videoProps = { + onLoadedMetadata: () => { + setWidth(videoRef.current?.videoWidth); + setHeight(videoRef.current?.videoHeight); + setDuration(videoRef.current?.duration); + }, + onLoadedData: () => { + const hasAudio = (videoRef.current as any).webkitAudioDecodedByteCount > 0 || Boolean( + (videoRef.current as any).audioTracks && + (videoRef.current as any).audioTracks.length > 0 + ); + + if (!hasAudio) { + videoRef.current.muted = true; + } + + setHasAudio(hasAudio); + } + }; + + return { + width, + height, + hasAudio, + duration, + setVideoRef, + videoProps + }; +}; + +const VideoMetadataContainer = createContainer(useVideoMetadata); + +export default VideoMetadataContainer; + diff --git a/renderer/components/editor/video-player.js b/renderer/components/editor/video-player.js deleted file mode 100644 index fb5695e36..000000000 --- a/renderer/components/editor/video-player.js +++ /dev/null @@ -1,67 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import classNames from 'classnames'; - -import Video from './video'; -import LeftControls from './controls/left'; -import RightControls from './controls/right'; -import PlayBar from './controls/play-bar'; - -export default class VideoPlayer extends React.Component { - render() { - const {hover} = this.props; - - const className = classNames('video-controls', {hover}); - - return ( -
-
- ); - } -} - -VideoPlayer.propTypes = { - hover: PropTypes.bool -}; diff --git a/renderer/components/editor/video-player.tsx b/renderer/components/editor/video-player.tsx new file mode 100644 index 000000000..f59f383b3 --- /dev/null +++ b/renderer/components/editor/video-player.tsx @@ -0,0 +1,124 @@ +import Video from './video'; +import LeftControls from './controls/left'; +import RightControls from './controls/right'; +import PlayBar from './controls/play-bar'; + +const VideoPlayer = () => { + return ( +
+
+ ); +}; + +export default VideoPlayer; + +// Import PropTypes from 'prop-types'; +// import React from 'react'; +// import classNames from 'classnames'; + +// import Video from './video'; +// import LeftControls from './controls/left'; +// import RightControls from './controls/right'; +// import PlayBar from './controls/play-bar'; + +// export default class VideoPlayer extends React.Component { +// render() { +// const {hover} = this.props; + +// const className = classNames('video-controls', {hover}); + +// return ( +//
+//
+// ); +// } +// } + +// VideoPlayer.propTypes = { +// hover: PropTypes.bool +// }; diff --git a/renderer/components/editor/video-time-container.tsx b/renderer/components/editor/video-time-container.tsx new file mode 100644 index 000000000..1be5acb4e --- /dev/null +++ b/renderer/components/editor/video-time-container.tsx @@ -0,0 +1,86 @@ +import {createContainer} from 'unstated-next'; +import {useRef, useState, useEffect} from 'react'; + +const useVideoTime = () => { + const videoRef = useRef(); + + const [startTime, setStartTime] = useState(0); + const [endTime, setEndTime] = useState(0); + const [duration, setDuration] = useState(0); + const [currentTime, setCurrentTime] = useState(0); + + const setVideoRef = (video: HTMLVideoElement) => { + videoRef.current = video; + }; + + const videoProps = { + onLoadedMetadata: () => { + if (duration === 0) { + setDuration(videoRef.current?.duration); + setEndTime(videoRef.current?.duration); + } + }, + onEnded: () => { + updateTime(startTime); + } + }; + + const updateTime = (time: number, ignoreElement = false) => { + if (time >= endTime && !videoRef.current.paused) { + videoRef.current.currentTime = startTime; + setCurrentTime(startTime); + } else { + if (!ignoreElement) { + videoRef.current.currentTime = time; + } + + setCurrentTime(time); + } + }; + + const updateStartTime = (time: number) => { + if (time < endTime) { + videoRef.current.currentTime = time; + setStartTime(time); + setCurrentTime(time); + } + }; + + const updateEndTime = (time: number) => { + if (time > startTime) { + videoRef.current.currentTime = time; + setEndTime(time); + setCurrentTime(time); + } + }; + + useEffect(() => { + if (!videoRef.current) { + return; + } + + const interval = setInterval(() => { + updateTime(videoRef.current.currentTime ?? 0, true); + }, 1000 / 30); + + return () => { + clearInterval(interval); + }; + }, [startTime, endTime]); + + return { + startTime, + endTime, + duration, + currentTime, + updateTime, + updateStartTime, + updateEndTime, + setVideoRef, + videoProps + }; +}; + +const VideoTimeContainer = createContainer(useVideoTime); + +export default VideoTimeContainer; diff --git a/renderer/components/editor/video.js b/renderer/components/editor/video.js deleted file mode 100644 index f4e41a5bb..000000000 --- a/renderer/components/editor/video.js +++ /dev/null @@ -1,86 +0,0 @@ -import electron from 'electron'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import {connect, VideoContainer, EditorContainer} from '../../containers'; - -class Video extends React.Component { - constructor(props) { - super(props); - this.videoRef = React.createRef(); - } - - componentDidMount() { - const {remote} = electron; - const {Menu, MenuItem} = remote; - const {getSnapshot} = this.props; - - this.menu = new Menu(); - this.menu.append(new MenuItem({label: 'Snapshot', click: getSnapshot})); - } - - componentDidUpdate(previousProps) { - const {setVideo, src} = this.props; - - if (!previousProps.src && src) { - setVideo(this.videoRef.current); - } - } - - contextMenu = () => { - const {play, pause} = this.props; - const video = this.videoRef.current; - const wasPaused = video.paused; - - if (!wasPaused) { - pause(); - } - - this.menu.popup({ - callback: () => { - if (!wasPaused) { - play(); - } - } - }); - } - - render() { - const {src} = this.props; - - if (!src) { - return null; - } - - return ( -
-
- ); - } -} - -Video.propTypes = { - src: PropTypes.string, - setVideo: PropTypes.elementType, - getSnapshot: PropTypes.elementType, - play: PropTypes.elementType, - pause: PropTypes.elementType -}; - -export default connect( - [VideoContainer, EditorContainer], - ({src}) => ({src}), - ({setVideo, play, pause}, {getSnapshot}) => ({setVideo, getSnapshot, play, pause}) -)(Video); diff --git a/renderer/components/editor/video.tsx b/renderer/components/editor/video.tsx new file mode 100644 index 000000000..82da024ef --- /dev/null +++ b/renderer/components/editor/video.tsx @@ -0,0 +1,97 @@ +import {useRef, useEffect} from 'react'; +import VideoTimeContainer from './video-time-container'; +import VideoMetadataContainer from './video-metadata-container'; +import VideoControlsContainer from './video-controls-container'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; +import {ipcRenderer as ipc} from 'electron-better-ipc'; + +const getVideoProps = (propsArray: Array, HTMLVideoElement>>) => { + const handlers = new Map(); + + for (const props of propsArray) { + for (const [key, handler] of Object.entries(props)) { + if (!handlers.has(key)) { + handlers.set(key, []); + } + + handlers.get(key).push(handler); + } + } + + // eslint-disable-next-line unicorn/no-array-reduce + return [...handlers.entries()].reduce((acc, [key, handlerList]) => ({ + ...acc, + [key]: () => { + for (const handler of handlerList) { + handler?.(); + } + } + }), {}); +}; + +const Video = () => { + const videoRef = useRef(); + const {filePath} = useEditorWindowState(); + const src = `file://${filePath}`; + + const videoTimeContainer = VideoTimeContainer.useContainer(); + const videoMetadataContainer = VideoMetadataContainer.useContainer(); + const videoControlsContainer = VideoControlsContainer.useContainer(); + + useEffect(() => { + videoTimeContainer.setVideoRef(videoRef.current); + videoMetadataContainer.setVideoRef(videoRef.current); + videoControlsContainer.setVideoRef(videoRef.current); + }, []); + + const videoProps = getVideoProps([ + videoTimeContainer.videoProps, + videoMetadataContainer.videoProps, + videoControlsContainer.videoProps + ]); + + const onContextMenu = async () => { + const video = videoRef.current; + + if (!video) { + return; + } + + const wasPaused = video.paused; + + if (!wasPaused) { + await videoControlsContainer.pause(); + } + + const {Menu} = require('electron-util').api; + const menu = Menu.buildFromTemplate([{ + label: 'Snapshot', + click: () => { + ipc.callMain('save-snapshot', video.currentTime); + } + }]); + + menu.popup({ + callback: () => { + if (!wasPaused) { + videoControlsContainer.play(); + } + } + }); + }; + + return ( +
+
+ ); +}; + +export default Video; diff --git a/renderer/components/exports/export.js b/renderer/components/exports/export.js index d879332de..06adde2a9 100644 --- a/renderer/components/exports/export.js +++ b/renderer/components/exports/export.js @@ -12,9 +12,9 @@ const stopPropagation = event => event.stopPropagation(); export default class Export extends React.Component { static defaultProps = { percentage: 0 - } + }; - state = {} + state = {}; componentDidMount() { const {remote} = electron; @@ -31,7 +31,7 @@ export default class Export extends React.Component { if (this.isActionable) { electron.remote.shell.showItemInFolder(filePath); } - } + }; onDragStart = event => { const {createdAt} = this.props; @@ -39,7 +39,7 @@ export default class Export extends React.Component { if (this.isActionable) { electron.ipcRenderer.send('drag-export', createdAt); } - } + }; get isActionable() { const {filePath, disableOutputActions} = this.props; diff --git a/renderer/components/icon-menu.js b/renderer/components/icon-menu.js deleted file mode 100644 index 2bc5c1d53..000000000 --- a/renderer/components/icon-menu.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -class IconMenu extends React.Component { - container = React.createRef(); - - openMenu = () => { - const boundingRect = this.container.current.children[0].getBoundingClientRect(); - const {bottom, left} = boundingRect; - const {onOpen} = this.props; - - if (onOpen) { - onOpen({ - x: Math.round(left), - y: Math.round(bottom) - }); - } - } - - render() { - const {icon: Icon, ...iconProps} = this.props; - return ( -
- - -
- ); - } -} - -IconMenu.propTypes = { - onOpen: PropTypes.elementType, - icon: PropTypes.elementType.isRequired, - fillParent: PropTypes.bool -}; - -export default IconMenu; diff --git a/renderer/components/icon-menu.tsx b/renderer/components/icon-menu.tsx new file mode 100644 index 000000000..66fff8836 --- /dev/null +++ b/renderer/components/icon-menu.tsx @@ -0,0 +1,37 @@ +import React, {FunctionComponent, useRef} from 'react'; +import {SvgProps} from 'vectors/svg'; + +interface IconMenuProps extends SvgProps { + onOpen: (options: {x: number; y: number}) => void; + icon: FunctionComponent; + fillParent?: boolean; +} + +const IconMenu: FunctionComponent = ({onOpen, icon: Icon, fillParent, ...iconProps}) => { + const container = useRef(null); + + const openMenu = () => { + const boundingRect = container.current.children[0].getBoundingClientRect(); + const {bottom, left} = boundingRect; + + onOpen({ + x: Math.round(left), + y: Math.round(bottom) + }); + }; + + return ( +
+ + +
+ ); +}; + +export default IconMenu; diff --git a/renderer/components/keyboard-number-input.js b/renderer/components/keyboard-number-input.js index a9a903d3c..7a828a4a8 100644 --- a/renderer/components/keyboard-number-input.js +++ b/renderer/components/keyboard-number-input.js @@ -10,13 +10,13 @@ class KeyboardNumberInput extends React.Component { getRef = () => { return this.inputRef; - } + }; render() { - const {onKeyDown, min, max, ...rest} = this.props; + const {onChange, min, max, ...rest} = this.props; return ( - + ); } } @@ -24,7 +24,8 @@ class KeyboardNumberInput extends React.Component { KeyboardNumberInput.propTypes = { onKeyDown: PropTypes.elementType, min: PropTypes.number, - max: PropTypes.number + max: PropTypes.number, + onChange: PropTypes.elementType }; export default KeyboardNumberInput; diff --git a/renderer/components/preferences/categories/general.js b/renderer/components/preferences/categories/general.js index a54b71ea4..353a2eec0 100644 --- a/renderer/components/preferences/categories/general.js +++ b/renderer/components/preferences/categories/general.js @@ -18,19 +18,19 @@ class General extends React.Component { audioDevices: [], kapturesDir: '', category: 'general' - } + }; - state = {} + state = {}; componentDidMount() { this.setState({ - showCursorSupported: electron.remote.require('./utils/macos-version').isGreaterThanOrEqualTo('10.13') + showCursorSupported: electron.remote.require('macos-version').isGreaterThanOrEqualTo('10.13') }); } openKapturesDir = () => { electron.shell.openItem(this.props.kapturesDir); - } + }; render() { const { diff --git a/renderer/components/preferences/categories/index.js b/renderer/components/preferences/categories/index.js index 2f136dc50..cafafd6c8 100644 --- a/renderer/components/preferences/categories/index.js +++ b/renderer/components/preferences/categories/index.js @@ -21,7 +21,7 @@ class Categories extends React.Component { componentDidUpdate(previousProps) { if (!previousProps.isMounted && this.props.isMounted) { // Wait for the transitions to end - setTimeout(() => ipc.callMain('preferences-ready'), 300); + setTimeout(async () => ipc.callMain('preferences-ready'), 300); } } diff --git a/renderer/components/preferences/categories/plugins/index.js b/renderer/components/preferences/categories/plugins/index.js index e58afcf4a..d3baf5a4b 100644 --- a/renderer/components/preferences/categories/plugins/index.js +++ b/renderer/components/preferences/categories/plugins/index.js @@ -11,7 +11,7 @@ class Plugins extends React.Component { pluginsInstalled: [], pluginsFromNpm: [], category: 'general' - } + }; render() { const { diff --git a/renderer/components/preferences/item/color-picker.js b/renderer/components/preferences/item/color-picker.js index 71ca1b298..db1998e59 100644 --- a/renderer/components/preferences/item/color-picker.js +++ b/renderer/components/preferences/item/color-picker.js @@ -22,7 +22,9 @@ const ColorPicker = ({hasErrors, value, onChange}) => { value={value.startsWith('#') ? value.slice(1, value.length) : value} size={7} onChange={handleChange} - onMouseUp={event => event.currentTarget.select()} + onMouseUp={event => { + event.currentTarget.select(); + }} /> -
- ); - } -} diff --git a/renderer/components/traffic-lights.tsx b/renderer/components/traffic-lights.tsx new file mode 100644 index 000000000..adb8457e8 --- /dev/null +++ b/renderer/components/traffic-lights.tsx @@ -0,0 +1,195 @@ +import {remote} from 'electron'; +import {useState, useEffect, FunctionComponent} from 'react'; + +interface TrafficLightsProps { + shouldClose?: () => PromiseLike; +} + +const TrafficLights: FunctionComponent = props => { + const currentWindow = remote.getCurrentWindow(); + const [tint, setTint] = useState('blue'); + + useEffect(() => { + const setTintColor = () => { + setTint(remote.systemPreferences.getUserDefault('AppleAquaColorVariant', 'string') === '6' ? 'graphite' : 'blue'); + }; + + const tintSubscription = remote.systemPreferences.subscribeNotification('AppleAquaColorVariantChanged', setTintColor); + setTintColor(); + + return () => { + remote.systemPreferences.unsubscribeNotification(tintSubscription); + }; + }, []); + + const enabled = { + close: currentWindow.isClosable(), + minimize: currentWindow.isMinimizable(), + maximize: currentWindow.isMaximizable() + }; + + const getClassName = (name: string) => `traffic-light ${name}${enabled[name] ? '' : ' disabled'}`; + + const close = async () => { + if (!props.shouldClose || await props.shouldClose()) { + currentWindow.close(); + } + }; + + const minimize = () => { + currentWindow.minimize(); + }; + + const maximize = () => { + currentWindow.setFullScreen(!currentWindow.isFullScreen()); + }; + + return ( +
+
+ + + + + +
+
+ + + + +
+
+ + + + + +
+ +
+ ); +}; + +export default TrafficLights; diff --git a/renderer/containers/action-bar.js b/renderer/containers/action-bar.js index 42c4f3f41..acc874b2d 100644 --- a/renderer/containers/action-bar.js +++ b/renderer/containers/action-bar.js @@ -5,7 +5,7 @@ const barWidth = 464; const barHeight = 64; export default class ActionBarContainer extends Container { - remote = electron.remote || false + remote = electron.remote || false; constructor() { super(); @@ -15,7 +15,7 @@ export default class ActionBarContainer extends Container { return; } - this.settings = this.remote.require('./common/settings'); + this.settings = this.remote.require('./common/settings').settings; this.state = { cropperWidth: '', cropperHeight: '' @@ -27,11 +27,15 @@ export default class ActionBarContainer extends Container { cropperWidth: width ? width.toString() : '', cropperHeight: height ? height.toString() : '' }); - } + }; - setWidth = cropperWidth => this.setState({cropperWidth}) + setWidth = cropperWidth => { + this.setState({cropperWidth}); + }; - setHeight = cropperHeight => this.setState({cropperHeight}) + setHeight = cropperHeight => { + this.setState({cropperHeight}); + }; setDisplay = display => { const {width, height, cropper} = display; @@ -46,7 +50,7 @@ export default class ActionBarContainer extends Container { height: barHeight, ratioLocked }); - } + }; resetPosition = () => { const {screenWidth, screenHeight} = this.state; @@ -57,15 +61,15 @@ export default class ActionBarContainer extends Container { width: barWidth, height: barHeight }); - } + }; bindCursor = cursorContainer => { this.cursorContainer = cursorContainer; - } + }; bindCropper = cropperContainer => { this.cropperContainer = cropperContainer; - } + }; updateSettings = updates => { const {x, y, ratioLocked} = this.state; @@ -77,7 +81,7 @@ export default class ActionBarContainer extends Container { ...updates }); this.setState(updates); - } + }; toggleRatioLock = ratioLocked => { const {ratioLocked: isLocked} = this.state; @@ -88,7 +92,7 @@ export default class ActionBarContainer extends Container { } this.cropperContainer.setOriginal(); - } + }; toggleAdvanced = () => { if (!this.cropperContainer.state.isFullscreen) { @@ -104,19 +108,19 @@ export default class ActionBarContainer extends Container { }); } } - } + }; startMoving = ({pageX, pageY}) => { this.setState({isMoving: true, offsetX: pageX, offsetY: pageY}); this.cursorContainer.addCursorObserver(this.move); - } + }; stopMoving = () => { const {x, y} = this.state; this.updateSettings({x, y}); this.setState({isMoving: false}); this.cursorContainer.removeCursorObserver(this.move); - } + }; move = ({pageX, pageY}) => { const {x, y, offsetX, offsetY, height, width, screenWidth, screenHeight} = this.state; @@ -135,5 +139,5 @@ export default class ActionBarContainer extends Container { } this.setState(updates); - } + }; } diff --git a/renderer/containers/config.js b/renderer/containers/config.js index 87551ebdb..979e696e8 100644 --- a/renderer/containers/config.js +++ b/renderer/containers/config.js @@ -4,10 +4,10 @@ import {Container} from 'unstated'; export default class ConfigContainer extends Container { remote = electron.remote || false; - state = {selectedTab: 0} + state = {selectedTab: 0}; setPlugin(pluginName) { - const {InstalledPlugin} = this.remote.require('./plugin'); + const {InstalledPlugin} = this.remote.require('./plugins/plugin'); this.plugin = new InstalledPlugin(pluginName); this.config = this.plugin.config; this.validators = this.config.validators; @@ -20,7 +20,7 @@ export default class ConfigContainer extends Container { } setEditService = (pluginName, serviceTitle) => { - const {InstalledPlugin} = this.remote.require('./plugin'); + const {InstalledPlugin} = this.remote.require('./plugins/plugin'); this.plugin = new InstalledPlugin(pluginName); this.config = this.plugin.config; this.validators = this.config.validators.filter(({title}) => title === serviceTitle); @@ -31,17 +31,17 @@ export default class ConfigContainer extends Container { pluginName, serviceTitle }); - } + }; validate = () => { for (const validator of this.validators) { - validator(this.config.store); + validator.validate(this.config.store); } - } + }; closeWindow = () => this.remote.getCurrentWindow().close(); - openConfig = () => this.plugin.openConfig(); + openConfig = () => this.plugin.openConfigInEditor(); viewOnGithub = () => this.plugin.viewOnGithub(); @@ -54,9 +54,9 @@ export default class ConfigContainer extends Container { this.validate(); this.setState({values: this.config.store}); - } + }; selectTab = selectedTab => { this.setState({selectedTab}); - } + }; } diff --git a/renderer/containers/cropper.js b/renderer/containers/cropper.js index 131e3fad3..03d4dc7ca 100644 --- a/renderer/containers/cropper.js +++ b/renderer/containers/cropper.js @@ -47,7 +47,9 @@ export default class CropperContainer extends Container { return; } - this.settings = this.remote.require('./common/settings'); + const {settings} = this.remote.require('./common/settings'); + this.settings = settings; + this.settings.getSelectedInputDeviceId = this.remote.require('./utils/devices').getSelectedInputDeviceId; this.state = { isRecording: false, @@ -93,15 +95,15 @@ export default class CropperContainer extends Container { ratio }); this.actionBarContainer.setInputValues({width, height}); - } + }; willStartRecording = () => { this.setState({willStartRecording: true}); - } + }; setRecording = () => { this.setState({isRecording: true}); - } + }; setActive = isActive => { const updates = {isActive}; @@ -117,7 +119,7 @@ export default class CropperContainer extends Container { } this.setState(updates); - } + }; updateSettings = updates => { const {x, y, width, height, ratio, displayId} = this.state; @@ -132,7 +134,7 @@ export default class CropperContainer extends Container { displayId }); this.setState(updates); - } + }; setSize = ({width: defaultWidth, height: defaultHeight}) => { let {width, height} = this.state; @@ -142,15 +144,15 @@ export default class CropperContainer extends Container { this.settings.set('cropper', updates); this.setState(updates); this.actionBarContainer.setInputValues(updates); - } + }; bindCursor = cursorContainer => { this.cursorContainer = cursorContainer; - } + }; bindActionBar = actionBarContainer => { this.actionBarContainer = actionBarContainer; - } + }; setBounds = (bounds, {save = true, ignoreRatioLocked} = {}) => { if (bounds) { @@ -173,7 +175,7 @@ export default class CropperContainer extends Container { } else { this.actionBarContainer.setInputValues({}); } - } + }; setRatio = ratio => { const {x, y, width, screenHeight} = this.state; @@ -192,7 +194,7 @@ export default class CropperContainer extends Container { this.updateSettings(updates); this.actionBarContainer.setInputValues(updates); this.actionBarContainer.toggleRatioLock(true); - } + }; swapDimensions = () => { const {x, y, width, height, ratio, screenHeight} = this.state; @@ -211,21 +213,23 @@ export default class CropperContainer extends Container { this.updateSettings(updates); this.actionBarContainer.setInputValues(updates); - } + }; selectApp = app => { const {x, y, width, height, ownerName} = app; this.setState({selectedApp: ownerName}); this.setBounds({x, y, width, height}, {ignoreRatioLocked: true}); - } + }; unselectApp = () => { if (this.state.selectedApp) { this.setState({selectedApp: ''}); } - } + }; - toggleResizeFromCenter = resizeFromCenter => this.setState({resizeFromCenter}); + toggleResizeFromCenter = resizeFromCenter => { + this.setState({resizeFromCenter}); + }; enterFullscreen = () => { const {x, y, width, height, screenWidth, screenHeight} = this.state; @@ -239,18 +243,18 @@ export default class CropperContainer extends Container { showHandles: false, original: {x, y, width, height} }); - } + }; exitFullscreen = () => { const {original} = this.state; this.setState({isFullscreen: false, showHandles: true, ...original}); - } + }; startPicking = ({pageX, pageY}) => { this.unselectApp(); this.setState({isPicking: true, original: {pageX, pageY}}); this.cursorContainer.addCursorObserver(this.pick); - } + }; pick = ({pageX, pageY}) => { const {original, isPicking} = this.state; @@ -273,7 +277,7 @@ export default class CropperContainer extends Container { this.setOriginal(); this.cursorContainer.addCursorObserver(this.resize); } - } + }; stopPicking = () => { if (this.state.isPicking) { @@ -281,12 +285,12 @@ export default class CropperContainer extends Container { } else { this.cursorContainer.removeCursorObserver(this.pick); } - } + }; setOriginal = () => { const {x, y, width, height} = this.state; this.setState({original: {x, y, width, height}}); - } + }; startResizing = currentHandle => { if (!this.state.isFullscreen) { @@ -295,7 +299,7 @@ export default class CropperContainer extends Container { this.setState({currentHandle, isResizing: true}); this.cursorContainer.addCursorObserver(this.resize); } - } + }; stopResizing = () => { if (!this.state.isFullscreen && this.state.isResizing) { @@ -310,7 +314,7 @@ export default class CropperContainer extends Container { ratio }); } - } + }; startMoving = ({pageX, pageY}) => { if (!this.state.isFullscreen) { @@ -318,7 +322,7 @@ export default class CropperContainer extends Container { this.setState({isMoving: true, showHandles: false, offsetX: pageX, offsetY: pageY}); this.cursorContainer.addCursorObserver(this.move); } - } + }; stopMoving = () => { if (!this.state.isFullscreen && this.state.isMoving) { @@ -328,7 +332,7 @@ export default class CropperContainer extends Container { this.cursorContainer.removeCursorObserver(this.move); this.updateSettings({x, y}); } - } + }; move = ({pageX, pageY}) => { const {x, y, offsetX, offsetY, width, height, screenWidth, screenHeight} = this.state; @@ -347,7 +351,7 @@ export default class CropperContainer extends Container { } this.setBounds(updates, {save: false}); - } + }; resize = ({pageX, pageY}) => { const {currentHandle, x, y, width, height, original, ratio, screenWidth, screenHeight, resizeFromCenter} = this.state; @@ -511,5 +515,5 @@ export default class CropperContainer extends Container { } this.setBounds(updates, {save: false}); - } + }; } diff --git a/renderer/containers/cursor.js b/renderer/containers/cursor.js index c9bc40d4a..d5158a269 100644 --- a/renderer/containers/cursor.js +++ b/renderer/containers/cursor.js @@ -8,15 +8,15 @@ export default class CursorContainer extends Container { setCursor = ({pageX, pageY}) => { this.setState({cursorX: pageX, cursorY: pageY}); this.state.observers.forEach(observer => observer({pageX, pageY})); - } + }; addCursorObserver = observer => { const {observers} = this.state; this.setState({observers: [observer, ...observers]}); - } + }; removeCursorObserver = observer => { const {observers} = this.state; this.setState({observers: observers.filter(o => o !== observer)}); - } + }; } diff --git a/renderer/containers/editor.js b/renderer/containers/editor.js index c94b8f28c..7c6a205ba 100644 --- a/renderer/containers/editor.js +++ b/renderer/containers/editor.js @@ -6,11 +6,11 @@ import {shake} from '../utils/inputs'; const isMuted = format => ['gif', 'apng'].includes(format); export default class EditorContainer extends Container { - state = {} + state = {}; setVideoContainer = videoContainer => { this.videoContainer = videoContainer; - } + }; mount = ({filePath, fps = 15, originalFilePath, isNewRecording, recordingName, title}, resolve) => { const src = `file://${filePath}`; @@ -22,19 +22,17 @@ export default class EditorContainer extends Container { originalFilePath, recordingName, title, - // TODO: Fix this ESLint violation - // eslint-disable-next-line react/no-access-state-in-setstate fps: Math.min(fps, this.state.fps), originalFps: fps, wasMuted: false, isNewRecording }); this.videoContainer.setSrc(src); - } + }; setDimensions = (width, height) => { this.setState({width, height, ratio: width / height, original: {width, height}}); - } + }; changeDimension = (event, {ignoreEmpty = true} = {}) => { const {ratio, original, lastValid = {}} = this.state; @@ -102,7 +100,7 @@ export default class EditorContainer extends Container { } this.setState(updates); - } + }; openEditPluginConfig = async () => { const {editPlugin, filePath} = this.state; @@ -114,7 +112,7 @@ export default class EditorContainer extends Container { }); ipc.callMain('refresh-usage'); - } + }; setOptions = ({exportOptions = [], editOptions = [], fps}) => { const {format, plugin, editPlugin} = this.state; @@ -123,7 +121,7 @@ export default class EditorContainer extends Container { if (format) { const option = exportOptions.find(option => option.format === format); - if (!option.plugins.find(p => p.title === plugin)) { + if (!option.plugins.some(p => p.title === plugin)) { const [{title}, {title: secondTitle} = {}] = option.plugins; updates.plugin = title === 'Open With' ? secondTitle : title; } @@ -143,17 +141,17 @@ export default class EditorContainer extends Container { } this.setState(updates); - } + }; saveOriginal = () => { const {filePath, originalFilePath} = this.state; ipc.callMain('save-original', {inputPath: originalFilePath || filePath}); - } + }; selectFormat = format => { const {plugin, options, wasMuted, fpsOptions, originalFps} = this.state; const {plugins} = options.find(option => option.format === format); - const newPlugin = plugin !== 'Open With' && plugins.find(p => p.title === plugin) ? plugin : plugins[0].title; + const newPlugin = plugin !== 'Open With' && plugins.some(p => p.title === plugin) ? plugin : plugins[0].title; if (this.videoContainer.state.hasAudio) { if (isMuted(format) && !isMuted(this.state.format)) { @@ -171,7 +169,7 @@ export default class EditorContainer extends Container { } this.setState(updates); - } + }; selectPlugin = plugin => { if (plugin === 'open-plugins') { @@ -179,15 +177,15 @@ export default class EditorContainer extends Container { } else { this.setState({plugin, openWithApp: null}); } - } + }; selectEditPlugin = editPlugin => { this.setState({editPlugin}); - } + }; selectOpenWithApp = openWithApp => { this.setState({plugin: 'Open With', openWithApp}); - } + }; setFps = (value, target, {ignoreEmpty = true} = {}) => { const {fps, lastValidFps} = this.state; @@ -219,11 +217,11 @@ export default class EditorContainer extends Container { } else { shake(target); } - } + }; load = () => { this.finishLoading(); - } + }; getSnapshot = () => { const time = this.videoContainer.state.currentTime; @@ -233,7 +231,7 @@ export default class EditorContainer extends Container { inputPath: filePath, time }); - } + }; startExport = () => { const { @@ -284,5 +282,5 @@ export default class EditorContainer extends Container { ipc.callMain('export', data); ipc.callMain('update-usage', {format, plugin: plugin.pluginName, fps}); - } + }; } diff --git a/renderer/containers/exports.js b/renderer/containers/exports.js index 6dc0162f7..ab03f0667 100644 --- a/renderer/containers/exports.js +++ b/renderer/containers/exports.js @@ -4,7 +4,7 @@ import {ipcRenderer as ipc} from 'electron-better-ipc'; export default class ExportsContainer extends Container { state = { exports: [] - } + }; mount = async () => { const exports = await ipc.callMain('get-exports'); @@ -14,9 +14,10 @@ export default class ExportsContainer extends Container { isMounted: true }); - const {ipcRenderer} = require('electron'); - ipcRenderer.on('update-export', (_, updates) => this.update(updates)); - } + ipc.answerMain('update-export-data', updates => { + this.update(updates); + }); + }; update = updates => { const {createdAt} = updates; @@ -34,13 +35,13 @@ export default class ExportsContainer extends Container { } this.setState({exports}); - } + }; cancel = createdAt => { ipc.callMain('cancel-export', createdAt); - } + }; openInEditor = createdAt => { ipc.callMain('open-export', createdAt); - } + }; } diff --git a/renderer/containers/preferences.js b/renderer/containers/preferences.js index a91cc9213..34d81b4f0 100644 --- a/renderer/containers/preferences.js +++ b/renderer/containers/preferences.js @@ -1,7 +1,9 @@ import electron from 'electron'; import {Container} from 'unstated'; import {ipcRenderer as ipc} from 'electron-better-ipc'; -import {defaultInputDeviceId} from '../../main/common/constants'; +// Import {defaultInputDeviceId} from 'common/constants'; + +const defaultInputDeviceId = 'asd'; const SETTINGS_ANALYTICS_BLACKLIST = ['kapturesDir']; @@ -12,17 +14,19 @@ export default class PreferencesContainer extends Container { category: 'general', tab: 'discover', isMounted: false - } + }; mount = async setOverlay => { this.setOverlay = setOverlay; - this.settings = this.remote.require('./common/settings'); + const {settings, shortcuts} = this.remote.require('./common/settings'); + this.settings = settings; + this.settings.shortcuts = shortcuts; this.systemPermissions = this.remote.require('./common/system-permissions'); - this.plugins = this.remote.require('./common/plugins'); + this.plugins = this.remote.require('./plugins').plugins; this.track = this.remote.require('./common/analytics').track; this.showError = this.remote.require('./utils/errors').showError; - const pluginsInstalled = this.plugins.getInstalled().sort((a, b) => a.prettyName.localeCompare(b.prettyName)); + const pluginsInstalled = this.plugins.installedPlugins.sort((a, b) => a.prettyName.localeCompare(b.prettyName)); this.fetchFromNpm(); @@ -38,7 +42,7 @@ export default class PreferencesContainer extends Container { if (this.settings.store.recordAudio) { this.getAudioDevices(); } - } + }; getAudioDevices = async () => { const {getAudioDevices, getDefaultInputDevice} = this.remote.require('./utils/devices'); @@ -60,7 +64,7 @@ export default class PreferencesContainer extends Container { } this.setState(updates); - } + }; scrollIntoView = (tabId, pluginId) => { const plugin = document.querySelector(`#${tabId} #${pluginId}`).parentElement; @@ -69,7 +73,7 @@ export default class PreferencesContainer extends Container { block: 'start', inline: 'nearest' }); - } + }; openTarget = target => { const isInstalled = this.state.pluginsInstalled.some(plugin => plugin.name === target.name); @@ -105,7 +109,7 @@ export default class PreferencesContainer extends Container { } else { this.setState({category: 'plugins'}); } - } + }; setNavigation = ({category, tab, target}) => { if (target) { @@ -117,7 +121,7 @@ export default class PreferencesContainer extends Container { } else { this.setState({category, tab}); } - } + }; fetchFromNpm = async () => { try { @@ -137,10 +141,10 @@ export default class PreferencesContainer extends Container { this.openTarget(this.state.target); this.setState({target: undefined}); } - } catch (_) { + } catch { this.setState({npmError: true}); } - } + }; togglePlugin = plugin => { if (plugin.isInstalled) { @@ -148,7 +152,7 @@ export default class PreferencesContainer extends Container { } else { this.install(plugin.name); } - } + }; install = async name => { const {pluginsInstalled, pluginsFromNpm} = this.state; @@ -167,7 +171,7 @@ export default class PreferencesContainer extends Container { pluginBeingInstalled: undefined }); } - } + }; uninstall = async name => { const {pluginsInstalled, pluginsFromNpm} = this.state; @@ -183,7 +187,7 @@ export default class PreferencesContainer extends Container { }; this.setState({pluginBeingUninstalled: name, onTransitionEnd}); - } + }; openPluginsConfig = async name => { this.track(`plugin/config/${name}`); @@ -193,22 +197,20 @@ export default class PreferencesContainer extends Container { await this.plugins.openPluginConfig(name); ipc.callMain('refresh-usage'); this.setOverlay(false); - } + }; - openPluginsFolder = () => electron.shell.openItem(this.plugins.cwd); + openPluginsFolder = () => electron.shell.openItem(this.plugins.pluginsDir); selectCategory = category => { this.setState({category}); - } + }; selectTab = tab => { this.track(`preferences/tab/${tab}`); this.setState({tab}); - } + }; toggleSetting = (setting, value) => { - // TODO: Fix this ESLint violation - // eslint-disable-next-line react/no-access-state-in-setstate const newValue = value === undefined ? !this.state[setting] : value; if (!SETTINGS_ANALYTICS_BLACKLIST.includes(setting)) { this.track(`preferences/setting/${setting}/${newValue}`); @@ -216,7 +218,7 @@ export default class PreferencesContainer extends Container { this.setState({[setting]: newValue}); this.settings.set(setting, newValue); - } + }; toggleRecordAudio = async () => { const newValue = !this.state.recordAudio; @@ -234,22 +236,20 @@ export default class PreferencesContainer extends Container { this.setState({recordAudio: newValue}); this.settings.set('recordAudio', newValue); } - } + }; toggleShortcuts = async () => { const setting = 'enableShortcuts'; const newValue = !this.state[setting]; this.toggleSetting(setting, newValue); await ipc.callMain('toggle-shortcuts', {enabled: newValue}); - } + }; updateShortcut = async (setting, shortcut) => { try { await ipc.callMain('update-shortcut', {setting, shortcut}); this.setState({ shortcuts: { - // TODO: Fix this ESLint violation - // eslint-disable-next-line react/no-access-state-in-setstate ...this.state.shortcuts, [setting]: shortcut } @@ -257,15 +257,13 @@ export default class PreferencesContainer extends Container { } catch (error) { console.warn('Error updating shortcut', error); } - } + }; setOpenOnStartup = value => { - // TODO: Fix this ESLint violation - // eslint-disable-next-line react/no-access-state-in-setstate const openOnStartup = typeof value === 'boolean' ? value : !this.state.openOnStartup; this.setState({openOnStartup}); this.remote.app.setLoginItemSettings({openAtLogin: openOnStartup}); - } + }; pickKapturesDir = () => { const {dialog, getCurrentWindow} = this.remote; @@ -280,10 +278,10 @@ export default class PreferencesContainer extends Container { if (directories) { this.toggleSetting('kapturesDir', directories[0]); } - } + }; setAudioInputDeviceId = id => { this.setState({audioInputDeviceId: id}); this.settings.set('audioInputDeviceId', id); - } + }; } diff --git a/renderer/containers/video.js b/renderer/containers/video.js index 77481113f..1d1c576c1 100644 --- a/renderer/containers/video.js +++ b/renderer/containers/video.js @@ -13,9 +13,11 @@ export default class VideoContainer extends Container { setEditorContainer = editorContainer => { this.editorContainer = editorContainer; - } + }; - setSrc = src => this.setState({src}) + setSrc = src => { + this.setState({src}); + }; checkTime = () => { if (this.ticker) { @@ -24,7 +26,7 @@ export default class VideoContainer extends Container { this.updateTime(this.video.currentTime); this.ticker = setTimeout(this.checkTime, 1000 / 120); - } + }; updateTime = currentTime => { const {startTime, endTime} = this.state; @@ -35,7 +37,7 @@ export default class VideoContainer extends Container { } else { this.setState({currentTime}); } - } + }; setStartTime = startTime => { const {endTime} = this.state; @@ -43,7 +45,7 @@ export default class VideoContainer extends Container { this.video.currentTime = startTime; this.setState({startTime, currentTime: startTime}); } - } + }; setEndTime = endTime => { const {startTime} = this.state; @@ -51,7 +53,7 @@ export default class VideoContainer extends Container { this.video.currentTime = endTime; this.setState({endTime, currentTime: endTime}); } - } + }; setVideo = video => { this.video = video; @@ -101,7 +103,7 @@ export default class VideoContainer extends Container { this.updateTime(endTime); this.play(); }); - } + }; play = async () => { try { @@ -113,7 +115,7 @@ export default class VideoContainer extends Container { } finally { this.playPromise = undefined; } - } + }; pause = async () => { if (this.playPromise) { @@ -122,20 +124,20 @@ export default class VideoContainer extends Container { this.video.pause(); this.setState({isPaused: true}); - } + }; mute = () => { this.setState({isMuted: true}); this.video.muted = true; - } + }; unmute = () => { this.setState({isMuted: false}); this.video.muted = false; - } + }; seek = time => { this.video.currentTime = time; this.updateTime(time); - } + }; } diff --git a/renderer/hooks/dark-mode.tsx b/renderer/hooks/dark-mode.tsx new file mode 100644 index 000000000..212fadb75 --- /dev/null +++ b/renderer/hooks/dark-mode.tsx @@ -0,0 +1,16 @@ +import {useState, useEffect} from 'react'; + +const useDarkMode = () => { + const {darkMode} = require('electron-util'); + const [isDarkMode, setIsDarkMode] = useState(darkMode.isEnabled); + + useEffect(() => { + return darkMode.onChange(() => { + setIsDarkMode(darkMode.isEnabled); + }); + }, []); + + return isDarkMode; +}; + +export default useDarkMode; diff --git a/renderer/hooks/editor/use-conversion-id.tsx b/renderer/hooks/editor/use-conversion-id.tsx new file mode 100644 index 000000000..fcb4ca8eb --- /dev/null +++ b/renderer/hooks/editor/use-conversion-id.tsx @@ -0,0 +1,41 @@ +import {CreateConversionOptions} from 'common/types'; +import {ipcRenderer} from 'electron-better-ipc'; +import {createContext, PropsWithChildren, useContext, useState} from 'react'; + +const ConversionIdContext = createContext<{ + conversionId: string; + setConversionId: (id: string) => void; + startConversion: (options: CreateConversionOptions) => Promise; +}>(undefined); + +let savedConversionId: string; + +export const ConversionIdContextProvider = (props: PropsWithChildren>) => { + const [conversionId, setConversionId] = useState(); + + const startConversion = async (options: CreateConversionOptions) => { + const id = await ipcRenderer.callMain('create-conversion', options); + setConversionId(id); + }; + + const setConvId = (id: string) => { + savedConversionId = savedConversionId || id; + setConversionId(id || savedConversionId); + }; + + const value = { + conversionId, + setConversionId: setConvId, + startConversion + }; + + return ( + + {props.children} + + ); +}; + +const useConversionIdContext = () => useContext(ConversionIdContext); + +export default useConversionIdContext; diff --git a/renderer/hooks/editor/use-conversion.tsx b/renderer/hooks/editor/use-conversion.tsx new file mode 100644 index 000000000..735e6c781 --- /dev/null +++ b/renderer/hooks/editor/use-conversion.tsx @@ -0,0 +1,8 @@ +import {ConversionRemoteState} from 'common/types'; +import createRemoteStateHook from 'hooks/use-remote-state'; + +const useConversion = createRemoteStateHook('conversion'); + +export type UseConversion = ReturnType; +export type UseConversionState = UseConversion['state']; +export default useConversion; diff --git a/renderer/hooks/editor/use-editor-options.tsx b/renderer/hooks/editor/use-editor-options.tsx new file mode 100644 index 000000000..4e27dd31d --- /dev/null +++ b/renderer/hooks/editor/use-editor-options.tsx @@ -0,0 +1,17 @@ +import {EditorOptionsRemoteState} from 'common/types'; +import createRemoteStateHook from 'hooks/use-remote-state'; + +const useEditorOptions = createRemoteStateHook('editor-options', { + formats: [], + editServices: [], + fpsHistory: { + gif: 60, + mp4: 60, + av1: 60, + webm: 60, + apng: 60 + } +}); + +export type EditorOptionsState = ReturnType['state']; +export default useEditorOptions; diff --git a/renderer/hooks/editor/use-editor-window-state.tsx b/renderer/hooks/editor/use-editor-window-state.tsx new file mode 100644 index 000000000..6013f97fa --- /dev/null +++ b/renderer/hooks/editor/use-editor-window-state.tsx @@ -0,0 +1,5 @@ +import useWindowState from 'hooks/window-state'; +import {EditorWindowState} from 'common/types'; + +const useEditorWindowState = () => useWindowState(); +export default useEditorWindowState; diff --git a/renderer/hooks/editor/use-share-plugins.tsx b/renderer/hooks/editor/use-share-plugins.tsx new file mode 100644 index 000000000..8960bb195 --- /dev/null +++ b/renderer/hooks/editor/use-share-plugins.tsx @@ -0,0 +1,83 @@ +import OptionsContainer from 'components/editor/options-container'; +import {remote} from 'electron'; +import {ipcRenderer} from 'electron-better-ipc'; +import {useMemo} from 'react'; + +const useSharePlugins = () => { + const { + formats, + format, + sharePlugin, + updateSharePlugin + } = OptionsContainer.useContainer(); + + const menuOptions = useMemo(() => { + const selectedFormat = formats.find(f => f.format === format); + + let onlyBuiltIn = true; + const options = selectedFormat?.plugins?.map(plugin => { + if (plugin.apps && plugin.apps.length > 0) { + const subMenu = plugin.apps.map(app => ({ + label: app.isDefault ? `${app.name} (default)` : app.name, + type: 'radio', + checked: sharePlugin.app?.url === app.url, + value: { + pluginName: plugin.pluginName, + serviceTitle: plugin.title, + app + }, + icon: remote.nativeImage.createFromDataURL(app.icon).resize({width: 16, height: 16}) + })); + + if (plugin.apps[0].isDefault) { + subMenu.splice(1, 0, {type: 'separator'} as any); + } + + return { + isBuiltIn: true, + subMenu, + value: { + pluginName: plugin.pluginName, + serviceTitle: plugin.title, + app: plugin.apps[0] + }, + checked: sharePlugin.pluginName === plugin.pluginName, + label: 'Open With…' + }; + } + + if (!plugin.pluginName.startsWith('_')) { + onlyBuiltIn = false; + } + + return { + value: { + pluginName: plugin.pluginName, + serviceTitle: plugin.title + }, + checked: sharePlugin.pluginName === plugin.pluginName, + label: plugin.title + }; + }); + + if (onlyBuiltIn) { + options?.push({ + separator: true + } as any, { + label: 'Get Plugins…', + checked: false, + click: () => { + ipcRenderer.callMain('open-preferences', {category: 'plugins', tab: 'discover'}); + } + } as any); + } + + return options ?? []; + }, [formats, format, sharePlugin]); + + const label = sharePlugin?.app ? sharePlugin.app.name : sharePlugin?.serviceTitle; + + return {menuOptions, label, onChange: updateSharePlugin}; +}; + +export default useSharePlugins; diff --git a/renderer/hooks/editor/use-window-size.tsx b/renderer/hooks/editor/use-window-size.tsx new file mode 100644 index 000000000..e5b18dc92 --- /dev/null +++ b/renderer/hooks/editor/use-window-size.tsx @@ -0,0 +1,40 @@ +import {remote} from 'electron'; +import {useEffect, useRef} from 'react'; +import {resizeKeepingCenter} from 'utils/window'; + +const CONVERSION_WIDTH = 360; +const CONVERSION_HEIGHT = 392; +const DEFAULT_EDITOR_WIDTH = 768; +const DEFAULT_EDITOR_HEIGHT = 480; + +export const useEditorWindowSizeEffect = (isConversionWindowState: boolean) => { + const previousWindowSizeRef = useRef<{width: number; height: number}>(); + + useEffect(() => { + if (!previousWindowSizeRef.current) { + previousWindowSizeRef.current = { + width: DEFAULT_EDITOR_WIDTH, + height: DEFAULT_EDITOR_HEIGHT + }; + return; + } + + const window = remote.getCurrentWindow(); + const bounds = window.getBounds(); + + if (isConversionWindowState) { + previousWindowSizeRef.current = { + width: bounds.width, + height: bounds.height + }; + + window.setBounds(resizeKeepingCenter(bounds, {width: CONVERSION_WIDTH, height: CONVERSION_HEIGHT}), true); + window.setResizable(false); + window.setFullScreenable(false); + } else { + window.setResizable(true); + window.setFullScreenable(true); + window.setBounds(resizeKeepingCenter(bounds, previousWindowSizeRef.current), true); + } + }, [isConversionWindowState]); +}; diff --git a/renderer/hooks/use-confirmation.tsx b/renderer/hooks/use-confirmation.tsx new file mode 100644 index 000000000..c55fa1d94 --- /dev/null +++ b/renderer/hooks/use-confirmation.tsx @@ -0,0 +1,33 @@ +import {useCallback} from 'react'; + +interface UseConfirmationOptions { + message: string; + detail?: string; + confirmButtonText: string; + cancelButtonText?: string; +} + +export const useConfirmation = ( + callback: () => void, + options: UseConfirmationOptions +) => { + return useCallback(() => { + const {dialog, remote} = require('electron-util').api; + + const buttonIndex = dialog.showMessageBoxSync(remote.getCurrentWindow(), { + type: 'question', + buttons: [ + options.confirmButtonText, + options.cancelButtonText ?? 'Cancel' + ], + defaultId: 0, + cancelId: 1, + message: options.message, + detail: options.detail + }); + + if (buttonIndex === 0) { + callback(); + } + }, [callback]); +}; diff --git a/renderer/hooks/use-current-window.tsx b/renderer/hooks/use-current-window.tsx new file mode 100644 index 000000000..39fbb9e7a --- /dev/null +++ b/renderer/hooks/use-current-window.tsx @@ -0,0 +1,5 @@ +import {remote} from 'electron'; + +export const useCurrentWindow = () => { + return remote.getCurrentWindow(); +}; diff --git a/renderer/hooks/use-keyboard-action.tsx b/renderer/hooks/use-keyboard-action.tsx new file mode 100644 index 000000000..ef976c9f0 --- /dev/null +++ b/renderer/hooks/use-keyboard-action.tsx @@ -0,0 +1,29 @@ +import {DependencyList, useEffect, useMemo} from 'react'; + +export const useKeyboardAction = (keyOrFilter: string | ((key: string, eventType: string) => boolean), action: () => void, deps: DependencyList = []) => { + const isArgFilter = typeof keyOrFilter === 'function'; + const filter = useMemo(() => typeof keyOrFilter === 'function' ? keyOrFilter : (key: string) => key === keyOrFilter, [keyOrFilter]); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (filter(event.key, event.type)) { + action(); + } + }; + + document.addEventListener('keyup', handler); + if (isArgFilter) { + document.addEventListener('keydown', handler); + document.addEventListener('keypress', handler); + } + + return () => { + document.removeEventListener('keyup', handler); + + if (isArgFilter) { + document.removeEventListener('keypress', handler); + document.removeEventListener('keydown', handler); + } + }; + }, [...deps, filter, action]); +}; diff --git a/renderer/hooks/use-remote-state.tsx b/renderer/hooks/use-remote-state.tsx new file mode 100644 index 000000000..e79ec86f0 --- /dev/null +++ b/renderer/hooks/use-remote-state.tsx @@ -0,0 +1,78 @@ +import {Promisable} from 'type-fest'; +import {useState, useEffect, useRef} from 'react'; +import {ipcRenderer} from 'electron-better-ipc'; + +// TODO: Import these util exports from the `main/remote-states/utils` file once we figure out the correct TS configuration +export const getChannelName = (name: string, action: string) => `kap-remote-state-${name}-${action}`; + +export const getChannelNames = (name: string) => ({ + subscribe: getChannelName(name, 'subscribe'), + getState: getChannelName(name, 'get-state'), + callAction: getChannelName(name, 'call-action'), + stateUpdated: getChannelName(name, 'state-updated') +}); + +// eslint-disable-next-line @typescript-eslint/ban-types +export type RemoteState> = (sendUpdate: (state?: State, id?: string) => void) => Promisable<{ + getState: (id?: string) => Promisable; + actions: Actions; + subscribe?: (id?: string) => undefined | (() => void); +}>; + +const createRemoteStateHook = >( + name: string, + initialState?: Callback extends RemoteState ? State : never +): (id?: string) => ( + Callback extends RemoteState ? ( + Actions & { + state: State; + isLoading: boolean; + refreshState: () => void; + } + ) : never +) => { + const channelNames = getChannelNames(name); + + return (id?: string) => { + const [state, setState] = useState(initialState); + const [isLoading, setIsLoading] = useState(true); + const actionsRef = useRef({}); + + useEffect(() => { + const cleanup = ipcRenderer.answerMain(channelNames.stateUpdated, setState); + + (async () => { + const actionKeys = (await ipcRenderer.callMain(channelNames.subscribe, id)); + + // eslint-disable-next-line unicorn/no-array-reduce + const actions = actionKeys.reduce((acc, key) => ({ + ...acc, + [key]: async (data: any) => ipcRenderer.callMain(channelNames.callAction, {key, data, id}) + }), {}); + + const getState = async () => { + const newState = (await ipcRenderer.callMain(channelNames.getState, id)); + setState(newState); + }; + + actionsRef.current = { + ...actions, + refreshState: getState + }; + + await getState(); + setIsLoading(false); + })(); + + return cleanup; + }, []); + + return { + ...actionsRef.current, + isLoading, + state + }; + }; +}; + +export default createRemoteStateHook; diff --git a/renderer/hooks/window-state.tsx b/renderer/hooks/window-state.tsx new file mode 100644 index 000000000..dfe526a77 --- /dev/null +++ b/renderer/hooks/window-state.tsx @@ -0,0 +1,27 @@ +import {createContext, useContext, useState, useEffect, ReactNode} from 'react'; +import {ipcRenderer as ipc} from 'electron-better-ipc'; + +const WindowStateContext = createContext(undefined); + +export const WindowStateProvider = (props: {children: ReactNode}) => { + const [windowState, setWindowState] = useState(); + + useEffect(() => { + return ipc.answerMain('kap-window-state', (newState: any) => { + setWindowState(newState); + }); + }, []); + + return ( + + {props.children} + + ); +}; + +// Should not be used directly +// Each page should export its own typed hook +// eslint-disable-next-line @typescript-eslint/comma-dangle +const useWindowState = () => useContext(WindowStateContext); + +export default useWindowState; diff --git a/renderer/next-env.d.ts b/renderer/next-env.d.ts new file mode 100644 index 000000000..7b7aa2c77 --- /dev/null +++ b/renderer/next-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/renderer/next.config.js b/renderer/next.config.js index 302032f82..67e3d8274 100644 --- a/renderer/next.config.js +++ b/renderer/next.config.js @@ -1,14 +1,25 @@ -exports.webpack = config => Object.assign(config, { - target: 'electron-renderer', - devtool: 'cheap-module-source-map', - plugins: config.plugins.filter(p => p.constructor.name !== 'UglifyJsPlugin') -}); - -exports.exportPathMap = () => ({ - '/cropper': {page: '/cropper'}, - '/editor': {page: '/editor'}, - '/preferences': {page: '/preferences'}, - '/exports': {page: '/exports'}, - '/config': {page: '/config'}, - '/dialog': {page: '/dialog'} -}); +const path = require('path'); + +module.exports = (nextConfig) => { + return Object.assign({}, nextConfig, { + webpack(config, options) { + config.module.rules.push({ + test: /\.+(js|jsx|mjs|ts|tsx)$/, + loader: options.defaultLoaders.babel, + include: [ + path.join(__dirname, '..', 'main', 'common'), + path.join(__dirname, '..', 'main', 'remote-states', 'use-remote-state.ts') + ] + }); + + config.target = 'electron-renderer'; + config.devtool = 'cheap-module-source-map'; + + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(config, options); + } + + return config; + } + }) +} diff --git a/renderer/pages/_app.tsx b/renderer/pages/_app.tsx new file mode 100644 index 000000000..5a699e1e1 --- /dev/null +++ b/renderer/pages/_app.tsx @@ -0,0 +1,46 @@ +import {AppProps} from 'next/app'; +import {useState, useEffect} from 'react'; +import useDarkMode from '../hooks/dark-mode'; +import GlobalStyles from '../utils/global-styles'; +import SentryErrorBoundary from '../utils/sentry-error-boundary'; +import {WindowStateProvider} from '../hooks/window-state'; +import classNames from 'classnames'; +import {ipcRenderer} from 'electron-better-ipc'; + +const Kap = (props: AppProps) => { + const [isMounted, setIsMounted] = useState(false); + + useEffect(() => { + setIsMounted(true); + }, []); + + useEffect(() => { + if (isMounted) { + ipcRenderer.callMain('kap-window-mount'); + } + }, [isMounted]); + + if (!isMounted) { + return null; + } + + return ; +}; + +const MainApp = ({Component, pageProps}: AppProps) => { + const isDarkMode = useDarkMode(); + const className = classNames('cover-window', {dark: isDarkMode}); + + return ( +
+ + + + + + +
+ ); +}; + +export default Kap; diff --git a/renderer/pages/config.js b/renderer/pages/config.js index 3394f5dfe..4858599d9 100644 --- a/renderer/pages/config.js +++ b/renderer/pages/config.js @@ -9,7 +9,7 @@ import WindowHeader from '../components/window-header'; const configContainer = new ConfigContainer(); export default class ConfigPage extends React.Component { - state = {title: ''} + state = {title: ''}; componentDidMount() { ipc.answerMain('plugin', pluginName => { diff --git a/renderer/pages/cropper.js b/renderer/pages/cropper.js index 3e17fface..a11e798e3 100644 --- a/renderer/pages/cropper.js +++ b/renderer/pages/cropper.js @@ -22,7 +22,7 @@ actionBarContainer.bindCropper(cropperContainer); let lastRatioLockState = null; export default class CropperPage extends React.Component { - remote = electron.remote || false + remote = electron.remote || false; dev = false; @@ -100,7 +100,7 @@ export default class CropperPage extends React.Component { default: break; } - } + }; render() { return ( diff --git a/renderer/pages/dialog.js b/renderer/pages/dialog.js index 075218f31..804c12e9d 100644 --- a/renderer/pages/dialog.js +++ b/renderer/pages/dialog.js @@ -6,13 +6,13 @@ import {ipcRenderer as ipc} from 'electron-better-ipc'; let measureResolve; -export default () => { +const Dialog = () => { const [data, setData] = useState(); const [measureSize, setMeasureSize] = useState(false); const [isDisabled, setIsDisabled] = useState(false); const container = useRef(null); - const performAction = index => { + const performAction = async index => { if (!isDisabled || data.cancelId === index) { setIsDisabled(true); return ipc.callMain(`dialog-action-${data.id}`, index); @@ -20,7 +20,7 @@ export default () => { }; useEffect(() => { - return ipc.answerMain('data', newData => new Promise(resolve => { + return ipc.answerMain('data', async newData => new Promise(resolve => { setData(newData); setMeasureSize(true); measureResolve = resolve; @@ -45,7 +45,7 @@ export default () => { document.removeEventListener('keydown', handler); }; } - }, [data]); + }, [data, performAction, isDisabled, setIsDisabled]); useEffect(() => { if (measureSize && measureResolve) { @@ -85,8 +85,6 @@ export default () => { display: flex; width: ${measureSize ? 'max-content' : '100%'}; height: ${measureSize ? 'max-content' : '100%'}; - min-width: 420px; - min-height: 150px; } .dialog-content { @@ -121,3 +119,5 @@ export default () => {
); }; + +export default Dialog; diff --git a/renderer/pages/editor.js b/renderer/pages/editor.js deleted file mode 100644 index f60d8e1ad..000000000 --- a/renderer/pages/editor.js +++ /dev/null @@ -1,202 +0,0 @@ -import React from 'react'; -import Head from 'next/head'; -import {Provider} from 'unstated'; -import {ipcRenderer as ipc} from 'electron-better-ipc'; - -import Editor from '../components/editor'; -import Options from '../components/editor/options'; -import {EditorContainer, VideoContainer} from '../containers'; - -const editorContainer = new EditorContainer(); -const videoContainer = new VideoContainer(); - -videoContainer.setEditorContainer(editorContainer); -editorContainer.setVideoContainer(videoContainer); - -export default class EditorPage extends React.Component { - wasPaused = false; - - componentDidMount() { - ipc.answerMain('file', async ({filePath, fps, originalFilePath, isNewRecording, recordingName, title}) => { - await new Promise((resolve, reject) => { - editorContainer.mount({filePath, fps: Number.parseInt(fps, 10), originalFilePath, isNewRecording, recordingName, title}, resolve, reject); - }); - return true; - }); - - ipc.answerMain('export-options', editorContainer.setOptions); - ipc.answerMain('save-original', editorContainer.saveOriginal); - - ipc.answerMain('blur', () => { - this.wasPaused = videoContainer.state.isPaused; - videoContainer.pause(); - }); - ipc.answerMain('focus', () => { - if (!this.wasPaused) { - videoContainer.play(); - } - }); - } - - render() { - return ( -
- - - -
- -
- -
-
- -
-
-
- -
- ); - } -} diff --git a/renderer/pages/editor.tsx b/renderer/pages/editor.tsx new file mode 100644 index 000000000..493aea9ce --- /dev/null +++ b/renderer/pages/editor.tsx @@ -0,0 +1,77 @@ +import Head from 'next/head'; +// Import EditorPreview from '../components/editor/editor-preview'; +import combineUnstatedContainers from '../utils/combine-unstated-containers'; +import VideoMetadataContainer from '../components/editor/video-metadata-container'; +import VideoTimeContainer from '../components/editor/video-time-container'; +import VideoControlsContainer from '../components/editor/video-controls-container'; +import OptionsContainer from '../components/editor/options-container'; +import useEditorWindowState from 'hooks/editor/use-editor-window-state'; +import {ConversionIdContextProvider} from 'hooks/editor/use-conversion-id'; +import Editor from 'components/editor'; + +const ContainerProvider = combineUnstatedContainers([ + OptionsContainer, + VideoMetadataContainer, + VideoTimeContainer, + VideoControlsContainer +]) as any; + +const EditorPage = () => { + const args = useEditorWindowState(); + + if (!args) { + return null; + } + + return ( +
+ + + + + + + + + +
+ ); +}; + +export default EditorPage; diff --git a/renderer/pages/exports.js b/renderer/pages/exports.js index 70351931c..33b21ed7b 100644 --- a/renderer/pages/exports.js +++ b/renderer/pages/exports.js @@ -36,7 +36,6 @@ export default class ExportsPage extends React.Component { .cover-window { background-color: var(--window-background-color); - z-index: -2; display: flex; flex-direction: column; font-size: 1.4rem; diff --git a/renderer/pages/preferences.js b/renderer/pages/preferences.js index a0365571f..2c53b7b0a 100644 --- a/renderer/pages/preferences.js +++ b/renderer/pages/preferences.js @@ -12,15 +12,17 @@ import PreferencesContainer from '../containers/preferences'; const preferencesContainer = new PreferencesContainer(); export default class PreferencesPage extends React.Component { - state = {overlay: false} + state = {overlay: false}; componentDidMount() { ipc.answerMain('open-plugin-config', preferencesContainer.openPluginsConfig); ipc.answerMain('options', preferencesContainer.setNavigation); - ipc.answerMain('mount', () => preferencesContainer.mount(this.setOverlay)); + ipc.answerMain('mount', async () => preferencesContainer.mount(this.setOverlay)); } - setOverlay = overlay => this.setState({overlay}); + setOverlay = overlay => { + this.setState({overlay}); + }; render() { const {overlay} = this.state; @@ -51,7 +53,6 @@ export default class PreferencesPage extends React.Component { .cover-window { background-color: var(--window-background-color); - z-index: -2; display: flex; flex-direction: column; font-size: 1.4rem; diff --git a/renderer/tsconfig.eslint.json b/renderer/tsconfig.eslint.json new file mode 100644 index 000000000..d59f596aa --- /dev/null +++ b/renderer/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.js" + ] +} diff --git a/renderer/tsconfig.json b/renderer/tsconfig.json new file mode 100644 index 000000000..c09246ff9 --- /dev/null +++ b/renderer/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "target": "es2019", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "preserveSymlinks": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "downlevelIteration": true, + "baseUrl": ".", + "paths": { + "utils/*": ["./utils/*"], + "components/*": ["./components/*"], + "containers/*": ["./containers/*"], + "hooks/*": ["./hooks/*"], + "common/*": ["./common/*"], + "vectors": ["./vectors"] + } + }, + "exclude": [ + "node_modules", + "common-remote-states" + ], + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ] +} diff --git a/renderer/utils/combine-unstated-containers.tsx b/renderer/utils/combine-unstated-containers.tsx new file mode 100644 index 000000000..c229b5a70 --- /dev/null +++ b/renderer/utils/combine-unstated-containers.tsx @@ -0,0 +1,22 @@ +import React, {FunctionComponent, PropsWithChildren} from 'react'; +import {Container} from 'unstated-next'; + +type ContainerOrWithInitialState = Container | [Container, T]; + +const combineUnstatedContainers = (containers: ContainerOrWithInitialState[]) => ({children}: PropsWithChildren>) => { + // eslint-disable-next-line unicorn/no-array-reduce + return containers.reduce( + (tree, ContainerOrWithInitialState) => { + if (Array.isArray(ContainerOrWithInitialState)) { + const [Container, initialState] = ContainerOrWithInitialState; + return {tree}; + } + + return {tree}; + }, + // @ts-expect-error + children + ); +}; + +export default combineUnstatedContainers; diff --git a/renderer/pages/_app.js b/renderer/utils/global-styles.tsx similarity index 52% rename from renderer/pages/_app.js rename to renderer/utils/global-styles.tsx index f19610515..326c01652 100644 --- a/renderer/pages/_app.js +++ b/renderer/utils/global-styles.tsx @@ -1,113 +1,48 @@ -import electron from 'electron'; -import React from 'react'; -import App from 'next/app'; -import * as Sentry from '@sentry/browser'; - -const SENTRY_PUBLIC_DSN = 'https://2dffdbd619f34418817f4db3309299ce@sentry.io/255536'; - -const remote = electron.remote || false; - -const systemColorNames = [ - 'control-background', - 'control', - 'control-text', - 'disabled-control-text', - 'find-highlight', - 'grid', - 'header-text', - 'highlight', - 'keyboard-focus-indicator', - 'label', - 'link', - 'placeholder-text', - 'quaternary-label', - 'scrubber-textured-background', - 'secondary-label', - 'selected-content-background', - 'selected-control', - 'selected-control-text', - 'selected-menu-item-text', - 'selected-text-background', - 'selected-text', - 'separator', - 'shadow', - 'tertiary-label', - 'text-background', - 'text', - 'under-page-background', - 'unemphasized-selected-content-background', - 'unemphasized-selected-text-background', - 'unemphasized-selected-text', - 'window-background', - 'window-frame-text' -]; - -export default class Kap extends App { - state = {isDark: false} - - constructor(...args) { - super(...args); - - if (remote) { - // TODO: When we disable SSR, this can be a normal import - const {is, darkMode, api} = require('electron-util'); - const settings = remote.require('./common/settings'); - this.systemPreferences = api.systemPreferences; - - if (!is.development && settings.get('allowAnalytics')) { - const release = `${api.app.name}@${api.app.getVersion()}`.toLowerCase(); - Sentry.init({dsn: SENTRY_PUBLIC_DSN, release}); +import {useState, useEffect, useMemo} from 'react'; +import useDarkMode from '../hooks/dark-mode'; +import {remote} from 'electron'; + +const GlobalStyles = () => { + const [accentColor, setAccentColor] = useState(remote.systemPreferences.getAccentColor()); + const isDarkMode = useDarkMode(); + + const systemColors = useMemo(() => { + return systemColorNames + .map(name => `--system-${name}: ${remote.systemPreferences.getColor(name as any)};`) + .join('\n'); + }, [isDarkMode]); + + const updateAccentColor = (_, accentColor) => { + setAccentColor(accentColor); + }; + + useEffect(() => { + remote.systemPreferences.on('accent-color-changed', updateAccentColor); + + // Return () => { + // api.systemPreferences.off('accent-color-changed', updateAccentColor); + // }; + }, []); + + return ( + -
- ); - } -} + + @keyframes shake-left { + 10%, + 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, + 80% { + transform: translate3d(0, 0, 0); + } + + 30%, + 50%, + 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, + 60% { + transform: translate3d(0, 0, 0); + } + } + + @keyframes shake-right { + 10%, + 90% { + transform: translate3d(1px, 0, 0); + } + + 20%, + 80% { + transform: translate3d(0, 0, 0); + } + + 30%, + 50%, + 70% { + transform: translate3d(4px, 0, 0); + } + + 40%, + 60% { + transform: translate3d(0, 0, 0); + } + } + + .shake-left { + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + perspective: 1000px; + animation: shake-left 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; + } + + .shake-right { + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + perspective: 1000px; + animation: shake-right 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; + } + + @keyframes shake { + 10%, + 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, + 80% { + transform: translate3d(2px, 0, 0); + } + + 30%, + 50%, + 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, + 60% { + transform: translate3d(4px, 0, 0); + } + } + + .shake { + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + perspective: 1000px; + animation: shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; + } + + * { box-sizing: border-box; } + `} + ); +}; + +export default GlobalStyles; + +const systemColorNames = [ + 'control-background', + 'control', + 'control-text', + 'disabled-control-text', + 'find-highlight', + 'grid', + 'header-text', + 'highlight', + 'keyboard-focus-indicator', + 'label', + 'link', + 'placeholder-text', + 'quaternary-label', + 'scrubber-textured-background', + 'secondary-label', + 'selected-content-background', + 'selected-control', + 'selected-control-text', + 'selected-menu-item-text', + 'selected-text-background', + 'selected-text', + 'separator', + 'shadow', + 'tertiary-label', + 'text-background', + 'text', + 'under-page-background', + 'unemphasized-selected-content-background', + 'unemphasized-selected-text-background', + 'unemphasized-selected-text', + 'window-background', + 'window-frame-text' +]; diff --git a/renderer/utils/inputs.js b/renderer/utils/inputs.js index 114beea54..16bffd7b0 100644 --- a/renderer/utils/inputs.js +++ b/renderer/utils/inputs.js @@ -177,8 +177,16 @@ const handleInputKeyPress = (onChange, min, max) => event => { return onChange(event, {ignoreEmpty: false}); } + // Don't let shift key lock aspect ratio + if (event.key === 'Shift') { + event.stopPropagation(); + } + const multiplier = event.shiftKey ? 10 : 1; const parsedValue = Number.parseInt(event.currentTarget.value, 10); + if (parsedValue === Number.NaN) { + return; + } // Fake an onChange event if (event.key === 'ArrowUp') { @@ -188,11 +196,6 @@ const handleInputKeyPress = (onChange, min, max) => event => { event.currentTarget.value = `${Math.max(parsedValue - multiplier, min)}`; onChange(event); } - - // Don't let shift key lock aspect ratio - if (event.key === 'Shift') { - event.stopPropagation(); - } }; const handleKeyboardActivation = (onClick, {isMenu} = {}) => event => { diff --git a/renderer/utils/sentry-error-boundary.tsx b/renderer/utils/sentry-error-boundary.tsx new file mode 100644 index 000000000..ed45dbe2b --- /dev/null +++ b/renderer/utils/sentry-error-boundary.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import * as Sentry from '@sentry/browser'; +import electron from 'electron'; +import type {api as Api, is as Is} from 'electron-util'; + +const SENTRY_PUBLIC_DSN = 'https://2dffdbd619f34418817f4db3309299ce@sentry.io/255536'; + +class SentryErrorBoundary extends React.Component<{children: React.ReactNode}> { + constructor(props) { + super(props); + const {settings} = electron.remote.require('./common/settings'); + // Done in-line because this is used in _app + const {is, api} = require('electron-util') as { + api: typeof Api; + is: typeof Is; + }; + + if (!is.development && settings.get('allowAnalytics')) { + const release = `${api.app.name}@${api.app.getVersion()}`.toLowerCase(); + Sentry.init({dsn: SENTRY_PUBLIC_DSN, release}); + } + } + + componentDidCatch(error, errorInfo) { + console.log(error, errorInfo); + Sentry.configureScope(scope => { + for (const [key, value] of Object.entries(errorInfo)) { + scope.setExtra(key, value); + } + }); + + Sentry.captureException(error); + + // This is needed to render errors correctly in development / production + super.componentDidCatch(error, errorInfo); + } + + render() { + return this.props.children; + } +} + +export default SentryErrorBoundary; diff --git a/renderer/utils/window.ts b/renderer/utils/window.ts new file mode 100644 index 000000000..62082c5b1 --- /dev/null +++ b/renderer/utils/window.ts @@ -0,0 +1,15 @@ + +export const resizeKeepingCenter = ( + bounds: Electron.Rectangle, + newSize: {width: number; height: number} +): Electron.Rectangle => { + const cx = Math.round(bounds.x + (bounds.width / 2)); + const cy = Math.round(bounds.y + (bounds.height / 2)); + + return { + x: Math.round(cx - (newSize.width / 2)), + y: Math.round(cy - (newSize.height / 2)), + width: newSize.width, + height: newSize.height + }; +}; diff --git a/renderer/vectors/back-plain.tsx b/renderer/vectors/back-plain.tsx new file mode 100644 index 000000000..f8007e0a7 --- /dev/null +++ b/renderer/vectors/back-plain.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; +import Svg, {SvgProps} from './svg'; + +const BackPlainIcon: FunctionComponent = props => ( + + + + +); + +export default BackPlainIcon; diff --git a/renderer/vectors/index.js b/renderer/vectors/index.js index 3fbeaac8d..b91394867 100644 --- a/renderer/vectors/index.js +++ b/renderer/vectors/index.js @@ -23,6 +23,7 @@ import ErrorIcon from './error'; import OpenConfigIcon from './open-config'; import OpenOnGithubIcon from './open-on-github'; import HelpIcon from './help'; +import BackPlainIcon from './back-plain'; export { ApplicationsIcon, @@ -49,5 +50,6 @@ export { ErrorIcon, OpenConfigIcon, OpenOnGithubIcon, - HelpIcon + HelpIcon, + BackPlainIcon }; diff --git a/renderer/vectors/svg.js b/renderer/vectors/svg.js deleted file mode 100644 index 619bcd1b8..000000000 --- a/renderer/vectors/svg.js +++ /dev/null @@ -1,117 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import PropTypes from 'prop-types'; - -import {handleKeyboardActivation} from '../utils/inputs'; - -class Svg extends React.Component { - static defaultProps = { - fill: 'var(--icon-color)', - activeFill: 'var(--kap)', - hoverFill: 'var(--icon-hover-color)', - size: '24px', - active: false, - viewBox: '0 0 24 24', - tabIndex: -1 - } - - onClick = () => { - const {onClick} = this.props; - if (onClick) { - onClick(); - } - } - - stopPropagation = event => { - event.stopPropagation(); - } - - render() { - const { - fill, - size, - activeFill, - hoverFill, - active, - onClick, - children, - viewBox, - shadow, - tabIndex, - isMenu - } = this.props; - - const className = classNames({active, shadow, focusable: tabIndex >= 0}); - - return ( -
= 0 ? handleKeyboardActivation(onClick, {isMenu}) : undefined}> - - { children } - - -
- ); - } -} - -Svg.propTypes = { - fill: PropTypes.string, - size: PropTypes.string, - activeFill: PropTypes.string, - hoverFill: PropTypes.string, - active: PropTypes.bool, - children: PropTypes.any, - viewBox: PropTypes.string, - onClick: PropTypes.elementType, - shadow: PropTypes.bool, - tabIndex: PropTypes.number, - isMenu: PropTypes.bool -}; - -export default Svg; diff --git a/renderer/vectors/svg.tsx b/renderer/vectors/svg.tsx new file mode 100644 index 000000000..12aadbe56 --- /dev/null +++ b/renderer/vectors/svg.tsx @@ -0,0 +1,109 @@ +import React, {FunctionComponent} from 'react'; +import classNames from 'classnames'; + +import {handleKeyboardActivation} from '../utils/inputs'; + +const defaultProps: SvgProps = { + fill: 'var(--icon-color)', + activeFill: 'var(--kap)', + hoverFill: 'var(--icon-hover-color)', + size: '24px', + active: false, + viewBox: '0 0 24 24', + tabIndex: -1 +}; + +const stopPropagation = event => { + event.stopPropagation(); +}; + +const Svg: FunctionComponent = props => { + const { + fill, + size, + activeFill, + hoverFill, + active, + onClick, + children, + viewBox, + shadow, + tabIndex, + isMenu + } = { + ...defaultProps, + ...props + }; + + const className = classNames({active, shadow, focusable: tabIndex >= 0}); + + return ( +
= 0 ? handleKeyboardActivation(onClick, {isMenu}) : undefined}> + + {children} + + +
+ ); +}; + +export interface SvgProps { + fill?: string; + size?: string; + activeFill?: string; + hoverFill?: string; + active?: boolean; + viewBox?: string; + onClick?: () => void; + shadow?: boolean; + tabIndex?: number; + isMenu?: boolean; +} + +export default Svg; diff --git a/test.svg b/test.svg new file mode 100644 index 000000000..50b058cbf --- /dev/null +++ b/test.svg @@ -0,0 +1,25 @@ + + + + + + +G + + + +remote-states/index.js + +remote-states/index.js + + + +remote-states/index.js->remote-states/index.js + + + + + diff --git a/test/convert.js b/test/convert.ts similarity index 63% rename from test/convert.js rename to test/convert.ts index 0a02089c5..6557aad6d 100644 --- a/test/convert.js +++ b/test/convert.ts @@ -1,27 +1,30 @@ -'use strict'; +import {serial as testAny, TestInterface} from 'ava'; +import fs from 'fs'; +import path from 'path'; +import sinon from 'sinon'; +import uniqueString from 'unique-string'; -const {serial: test} = require('ava'); -const uniqueString = require('unique-string'); -const fs = require('fs'); -const sinon = require('sinon'); -const path = require('path'); +const test = testAny as TestInterface<{outputPath: string}>; -const {getVideoMetadata} = require('./helpers/video-utils'); -const {almostEquals} = require('./helpers/assertions'); -const {getFormatExtension} = require('../main/common/constants'); +import {getVideoMetadata} from './helpers/video-utils'; +import {almostEquals} from './helpers/assertions'; +import {getFormatExtension} from '../main/common/constants'; +import {Except, SetOptional} from 'type-fest'; +import {mockImport} from './helpers/mocks'; +import {Format} from '../main/common/types'; -const getRandomFileName = (ext = 'mp4') => `${uniqueString()}.${getFormatExtension(ext)}`; +const getRandomFileName = (ext: Format = Format.mp4) => `${uniqueString()}.${getFormatExtension(ext)}`; const input = path.resolve(__dirname, 'fixtures', 'input.mp4'); const retinaInput = path.resolve(__dirname, 'fixtures', 'input@2x.mp4'); -const {mockImport} = require('./helpers/mocks'); +mockImport('../common/analytics', 'analytics'); +mockImport('../plugins/service-context', 'service-context'); +mockImport('../plugins', 'plugins'); +const {settings} = mockImport('../common/settings', 'settings'); -mockImport('./common/analytics', 'analytics'); -mockImport('./service-context', 'service-context'); -const settings = mockImport('./common/settings', 'settings'); - -const {convertTo} = require('../main/convert'); +import {convertTo} from '../main/converters'; +import {ConvertOptions} from '../main/converters/utils'; test.afterEach.always(t => { if (t.context.outputPath && fs.existsSync(t.context.outputPath)) { @@ -29,12 +32,14 @@ test.afterEach.always(t => { } }); -const convert = (format, options) => { - return convertTo({ +const convert = async (format: Format, options: SetOptional, 'onCancel' | 'onProgress' | 'shouldMute'>) => { + return convertTo(format, { defaultFileName: getRandomFileName(format), onProgress: sinon.fake(), + onCancel: sinon.fake(), + shouldMute: true, ...options - }, format); + }); }; // MP4 @@ -42,8 +47,8 @@ const convert = (format, options) => { test('mp4: retina with sound', async t => { const onProgress = sinon.fake(); - t.context.outputPath = await convert('mp4', { - isMuted: false, + t.context.outputPath = await convert(Format.mp4, { + shouldMute: false, inputPath: retinaInput, fps: 39, width: 469, @@ -66,13 +71,13 @@ test('mp4: retina with sound', async t => { t.true(meta.hasAudio); - t.true(onProgress.calledWithMatch(sinon.match.number)); - t.true(onProgress.calledWithMatch(sinon.match.number, sinon.match.string)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number, sinon.match.string)); }); test('mp4: retina without sound', async t => { - t.context.outputPath = await convert('mp4', { - isMuted: true, + t.context.outputPath = await convert(Format.mp4, { + shouldMute: true, inputPath: retinaInput, fps: 10, width: 46, @@ -88,8 +93,8 @@ test('mp4: retina without sound', async t => { }); test('mp4: non-retina', async t => { - t.context.outputPath = await convert('mp4', { - isMuted: false, + t.context.outputPath = await convert(Format.mp4, { + shouldMute: false, inputPath: input, fps: 30, width: 255, @@ -118,8 +123,8 @@ test('mp4: non-retina', async t => { test('webm: retina with sound', async t => { const onProgress = sinon.fake(); - t.context.outputPath = await convert('webm', { - isMuted: false, + t.context.outputPath = await convert(Format.webm, { + shouldMute: false, inputPath: retinaInput, fps: 39, width: 469, @@ -132,8 +137,8 @@ test('webm: retina with sound', async t => { const meta = await getVideoMetadata(t.context.outputPath); - t.is(meta.size.width, 469); - t.is(meta.size.height, 839); + t.is(meta.size.width, 470); + t.is(meta.size.height, 840); t.is(meta.fps, 39); t.true(almostEquals(meta.duration, 13.5)); @@ -141,13 +146,13 @@ test('webm: retina with sound', async t => { t.true(meta.hasAudio); - t.true(onProgress.calledWithMatch(sinon.match.number)); - t.true(onProgress.calledWithMatch(sinon.match.number, sinon.match.string)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number, sinon.match.string)); }); test('webm: retina without sound', async t => { - t.context.outputPath = await convert('webm', { - isMuted: true, + t.context.outputPath = await convert(Format.webm, { + shouldMute: true, inputPath: retinaInput, fps: 10, width: 46, @@ -163,8 +168,8 @@ test('webm: retina without sound', async t => { }); test('webm: non-retina', async t => { - t.context.outputPath = await convert('webm', { - isMuted: false, + t.context.outputPath = await convert(Format.webm, { + shouldMute: false, inputPath: input, fps: 30, width: 255, @@ -176,8 +181,8 @@ test('webm: non-retina', async t => { const meta = await getVideoMetadata(t.context.outputPath); - t.is(meta.size.width, 255); - t.is(meta.size.height, 143); + t.is(meta.size.width, 256); + t.is(meta.size.height, 144); t.is(meta.fps, 30); t.true(almostEquals(meta.duration, 15.5)); @@ -191,8 +196,8 @@ test('webm: non-retina', async t => { test('apng: retina', async t => { const onProgress = sinon.fake(); - t.context.outputPath = await convert('apng', { - isMuted: false, + t.context.outputPath = await convert(Format.apng, { + shouldMute: false, inputPath: retinaInput, fps: 15, width: 469, @@ -213,13 +218,13 @@ test('apng: retina', async t => { t.false(meta.hasAudio); - t.true(onProgress.calledWithMatch(sinon.match.number)); - t.true(onProgress.calledWithMatch(sinon.match.number, sinon.match.string)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number, sinon.match.string)); }); test('apng: non-retina', async t => { - t.context.outputPath = await convert('apng', { - isMuted: false, + t.context.outputPath = await convert(Format.apng, { + shouldMute: false, inputPath: input, fps: 15, width: 255, @@ -245,8 +250,8 @@ test('apng: non-retina', async t => { test('gif: retina', async t => { const onProgress = sinon.fake(); - t.context.outputPath = await convert('gif', { - isMuted: false, + t.context.outputPath = await convert(Format.gif, { + shouldMute: false, inputPath: retinaInput, fps: 10, width: 236, @@ -268,13 +273,13 @@ test('gif: retina', async t => { t.false(meta.hasAudio); - t.true(onProgress.calledWithMatch(sinon.match.number)); - t.true(onProgress.calledWithMatch(sinon.match.number, sinon.match.string)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number, sinon.match.string)); }); test('gif: non-retina', async t => { - t.context.outputPath = await convert('gif', { - isMuted: false, + t.context.outputPath = await convert(Format.gif, { + shouldMute: false, inputPath: input, fps: 15, width: 255, @@ -297,30 +302,32 @@ test('gif: non-retina', async t => { }); test('gif: lossy', async t => { - const regular = await convert('gif', { + settings.setMock('lossyCompression', false); + + const regular = await convert(Format.gif, { inputPath: input, - fps: 15, + fps: 20, width: 510, height: 286, - startTime: 10, - endTime: 15, + startTime: 1, + endTime: 10, shouldCrop: true }); settings.setMock('lossyCompression', true); - const lossy = await convert('gif', { + const lossy = await convert(Format.gif, { inputPath: input, - fps: 15, + fps: 20, width: 510, height: 286, - startTime: 10, - endTime: 15, + startTime: 1, + endTime: 10, shouldCrop: true }); t.true( - fs.statSync(regular).size > + fs.statSync(regular).size >= fs.statSync(lossy).size ); }); @@ -330,8 +337,8 @@ test('gif: lossy', async t => { test('av1: retina with sound', async t => { const onProgress = sinon.fake(); - t.context.outputPath = await convert('av1', { - isMuted: false, + t.context.outputPath = await convert(Format.av1, { + shouldMute: false, inputPath: retinaInput, fps: 15, width: 235, @@ -354,13 +361,13 @@ test('av1: retina with sound', async t => { t.true(meta.hasAudio); - t.true(onProgress.calledWithMatch(sinon.match.number)); - t.true(onProgress.calledWithMatch(sinon.match.number, sinon.match.string)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number)); + t.true(onProgress.calledWithMatch(sinon.match.string, sinon.match.number, sinon.match.string)); }); test('av1: retina without sound', async t => { - t.context.outputPath = await convert('av1', { - isMuted: true, + t.context.outputPath = await convert(Format.av1, { + shouldMute: true, inputPath: retinaInput, fps: 10, width: 100, @@ -376,8 +383,8 @@ test('av1: retina without sound', async t => { }); test('av1: non-retina', async t => { - t.context.outputPath = await convert('av1', { - isMuted: false, + t.context.outputPath = await convert(Format.av1, { + shouldMute: false, inputPath: input, fps: 10, width: 255, diff --git a/test/helpers/assertions.js b/test/helpers/assertions.ts similarity index 67% rename from test/helpers/assertions.js rename to test/helpers/assertions.ts index 8b275afd3..6cccb1ddb 100644 --- a/test/helpers/assertions.js +++ b/test/helpers/assertions.ts @@ -1,6 +1,5 @@ -'use strict'; -module.exports.almostEquals = (actual, expected, threshold = 0.5) => { +export const almostEquals = (actual: number, expected: number, threshold = 0.5) => { return Math.abs(actual - expected) <= threshold ? true : ` Actual: ${actual} Expected: ${expected} diff --git a/test/helpers/mocks.js b/test/helpers/mocks.js deleted file mode 100644 index 109170f2a..000000000 --- a/test/helpers/mocks.js +++ /dev/null @@ -1,24 +0,0 @@ -const moduleAlias = require('module-alias'); - -const path = require('path'); -const fs = require('fs'); - -module.exports.mockModule = name => { - const mockModulePath = path.resolve(__dirname, '..', 'mocks', `${name}.js`); - if (!fs.existsSync(mockModulePath)) { - throw new Error(`Missing mock implementation of ${name} at ${mockModulePath}`); - } - - moduleAlias.addAlias(name, mockModulePath); - return require(mockModulePath); -}; - -module.exports.mockImport = (importPath, mock) => { - const mockModulePath = path.resolve(__dirname, '..', 'mocks', `${mock}.js`); - if (!fs.existsSync(mockModulePath)) { - throw new Error(`Missing mock implementation at ${mockModulePath}`); - } - - moduleAlias.addAlias(importPath, mockModulePath); - return require(mockModulePath); -}; diff --git a/test/helpers/mocks.ts b/test/helpers/mocks.ts new file mode 100644 index 000000000..a531c3680 --- /dev/null +++ b/test/helpers/mocks.ts @@ -0,0 +1,37 @@ +import moduleAlias from 'module-alias'; +import path from 'path'; +import fs from 'fs'; + +export const mockModule = (name: string) => { + const mockModulePathTypescript = path.resolve(__dirname, '..', 'mocks', `${name}.ts`); + const mockModulePath = path.resolve(__dirname, '..', 'mocks', `${name}.js`); + + const mockPath = [ + mockModulePathTypescript, + mockModulePath + ].find(p => fs.existsSync(p)); + + if (!mockPath) { + throw new Error(`Missing mock implementation at ${mockModulePath}`.replace('js', '(ts|js)')); + } + + moduleAlias.addAlias(name, mockPath); + return require(mockPath); +}; + +export const mockImport = (importPath: string, mock: string) => { + const mockModulePathTypescript = path.resolve(__dirname, '..', 'mocks', `${mock}.ts`); + const mockModulePath = path.resolve(__dirname, '..', 'mocks', `${mock}.js`); + + const mockPath = [ + mockModulePathTypescript, + mockModulePath + ].find(p => fs.existsSync(p)); + + if (!mockPath) { + throw new Error(`Missing mock implementation at ${mockModulePath}`.replace('js', '(ts|js)')); + } + + moduleAlias.addAlias(importPath, mockPath); + return require(mockPath); +}; diff --git a/test/helpers/video-utils.js b/test/helpers/video-utils.js deleted file mode 100644 index 6d0ae84c0..000000000 --- a/test/helpers/video-utils.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -const moment = require('moment'); -const ffmpeg = require('@ffmpeg-installer/ffmpeg'); -const execa = require('execa'); - -const ffmpegPath = ffmpeg.path; - -const getDuration = text => { - const durationString = /Duration: ([\d:.]*)/.exec(text)[1]; - return moment.duration(durationString).asSeconds(); -}; - -const getEncoding = text => /Stream.*Video: (.*?)[, ]/.exec(text)[1]; - -const getFps = text => { - const fpsString = /([\d.]*) fps/.exec(text)[1]; - return Number.parseFloat(fpsString); -}; - -const getSize = text => { - const sizeText = /Video:.*?, (\d*x\d*)/.exec(text)[1]; - const parts = sizeText.split('x'); - return { - width: Number.parseFloat(parts[0]), - height: Number.parseFloat(parts[1]) - }; -}; - -const getHasAudio = text => /Stream #.*: Audio/.test(text); - -module.exports.getVideoMetadata = async path => { - try { - await execa(ffmpegPath, ['-i', path]); - } catch (error) { - const {stderr} = error; - return { - duration: getDuration(stderr), - encoding: getEncoding(stderr), - fps: getFps(stderr), - size: getSize(stderr), - hasAudio: getHasAudio(stderr) - }; - } -}; diff --git a/test/helpers/video-utils.ts b/test/helpers/video-utils.ts new file mode 100644 index 000000000..b555456aa --- /dev/null +++ b/test/helpers/video-utils.ts @@ -0,0 +1,51 @@ +import moment from 'moment'; +import execa from 'execa'; + +const ffmpeg = require('@ffmpeg-installer/ffmpeg'); + +const ffmpegPath = ffmpeg.path; + +const getDuration = (text: string): number => { + const durationString = /Duration: ([\d:.]*)/.exec(text)?.[1]; + return moment.duration(durationString).asSeconds(); +}; + +const getEncoding = (text: string) => /Stream.*Video: (.*?)[, ]/.exec(text)?.[1]; + +const getFps = (text: string) => { + const fpsString = /([\d.]*) fps/.exec(text)?.[1]; + return Number.parseFloat(fpsString!); +}; + +const getSize = (text: string) => { + const sizeText = /Video:.*?, (\d*x\d*)/.exec(text)?.[1]!; + const parts = sizeText.split('x'); + return { + width: Number.parseFloat(parts[0]), + height: Number.parseFloat(parts[1]) + }; +}; + +const getHasAudio = (text: string) => /Stream #.*: Audio/.test(text); + +// @ts-expect-error +export const getVideoMetadata = async (path: string): Promise<{ + duration: number; + encoding: string; + fps: number; + size: {width: number; height: number}; + hasAudio: boolean; +}> => { + try { + await execa(ffmpegPath, ['-i', path]); + } catch (error) { + const {stderr} = error; + return { + duration: getDuration(stderr), + encoding: getEncoding(stderr)!, + fps: getFps(stderr)!, + size: getSize(stderr) as {width: number; height: number}, + hasAudio: getHasAudio(stderr)! + }; + } +}; diff --git a/test/mocks/analytics.js b/test/mocks/analytics.js deleted file mode 100644 index e2b9c94f7..000000000 --- a/test/mocks/analytics.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -module.exports = { - initializeAnalytics: () => {}, - track: () => {} -}; diff --git a/test/mocks/analytics.ts b/test/mocks/analytics.ts new file mode 100644 index 000000000..0e31f88f7 --- /dev/null +++ b/test/mocks/analytics.ts @@ -0,0 +1,3 @@ + +export const initializeAnalytics = () => {}; +export const track = () => {}; diff --git a/test/mocks/dialog.js b/test/mocks/dialog.ts similarity index 51% rename from test/mocks/dialog.js rename to test/mocks/dialog.ts index 26bc5a91e..749c74e3c 100644 --- a/test/mocks/dialog.js +++ b/test/mocks/dialog.ts @@ -1,10 +1,10 @@ -const sinon = require('sinon'); +import sinon from 'sinon'; -let dialogState; -let dialogResolve; -let waitForDialogResolve; +let dialogState: any; +let dialogResolve: any; +let waitForDialogResolve: any; -const showDialogFake = sinon.fake(async options => new Promise(resolve => { +export const showDialog = sinon.fake(async (options: any) => new Promise(resolve => { dialogState = options; dialogResolve = resolve; @@ -15,7 +15,7 @@ const showDialogFake = sinon.fake(async options => new Promise(resolve => { waitForDialogResolve = undefined; })); -const resolve = result => { +const resolve = (result: any) => { if (dialogResolve) { dialogResolve(result); } @@ -24,13 +24,13 @@ const resolve = result => { dialogState = undefined; }; -const fakeAction = async index => { +export const fakeAction = async (index: any) => { const button = dialogState.buttons[index]; - const action = button && button.action; + const action = button?.action; let wasCalled = false; if (action) { - await action(resolve, newState => { + await action(resolve, (newState: any) => { wasCalled = true; dialogState = newState; }); @@ -43,16 +43,8 @@ const fakeAction = async index => { } }; -const getCurrentState = () => dialogState; +export const getCurrentState = () => dialogState; -const waitForDialog = () => new Promise(resolve => { +export const waitForDialog = async () => new Promise(resolve => { waitForDialogResolve = resolve; }); - -module.exports = { - showDialog: showDialogFake, - fakeAction, - resolve, - getCurrentState, - waitForDialog -}; diff --git a/test/mocks/editor.js b/test/mocks/editor.js deleted file mode 100644 index 1a8cf7019..000000000 --- a/test/mocks/editor.js +++ /dev/null @@ -1,5 +0,0 @@ -const sinon = require('sinon'); - -module.exports = { - openEditorWindow: sinon.fake() -}; diff --git a/test/mocks/electron-store.js b/test/mocks/electron-store.js deleted file mode 100644 index 928bce5c5..000000000 --- a/test/mocks/electron-store.js +++ /dev/null @@ -1,23 +0,0 @@ -const sinon = require('sinon'); - -const mocks = {}; - -const getMock = sinon.fake((key, defaultValue) => mocks[key] || defaultValue); -const setMock = sinon.fake(); -const deleteMock = sinon.fake(); - -module.exports = class Store { - get = getMock; - set = setMock; - delete = deleteMock; - - mockGet = (key, result) => { - mocks[key] = result; - } - - clearMocks = () => { - for (const key of Object.keys(mocks)) { - delete mocks[key]; - } - } -}; diff --git a/test/mocks/electron-store.ts b/test/mocks/electron-store.ts new file mode 100644 index 000000000..c1ff308b0 --- /dev/null +++ b/test/mocks/electron-store.ts @@ -0,0 +1,59 @@ +import sinon from 'sinon'; + +const mocks: Record = {}; +const store: Record = {}; + +const getMock = sinon.fake( + (key: string, defaultValue: any) => mocks[key] ?? store[key] ?? defaultValue +); + +const setMock = sinon.fake( + (key: string, value: any) => { + store[key] = value; + } +); + +const deleteMock = sinon.fake( + (key: string) => { + delete store[key]; + } +); + +const clearMock = sinon.fake( + () => { + for (const key of Object.keys(store)) { + delete store[key]; + } + } +); + +export default class Store { + get = getMock; + set = setMock; + delete = deleteMock; + clear = clearMock; + + get store() { + return { + ...store, + ...mocks + }; + } + + static mockGet = (key: string, result: any) => { + mocks[key] = result; + }; + + static clearMocks = () => { + for (const key of Object.keys(mocks)) { + delete mocks[key]; + } + }; + + static mocks = { + get: getMock, + set: setMock, + delete: deleteMock, + clear: clearMock + }; +} diff --git a/test/mocks/electron.js b/test/mocks/electron.js deleted file mode 100644 index 8e14b8b73..000000000 --- a/test/mocks/electron.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; -const sinon = require('sinon'); -const tempy = require('tempy'); -const path = require('path'); - -const temporaryDir = tempy.directory(); - -process.env.TZ = 'America/New_York'; -process.versions.chrome = ''; - -module.exports = { - app: { - getPath: name => path.resolve(temporaryDir, name), - isPackaged: false - }, - shell: { - showItemInFolder: sinon.fake() - }, - clipboard: { - writeText: sinon.fake() - }, - remote: {} -}; diff --git a/test/mocks/electron.ts b/test/mocks/electron.ts new file mode 100644 index 000000000..5a85bc624 --- /dev/null +++ b/test/mocks/electron.ts @@ -0,0 +1,24 @@ +import sinon from 'sinon'; +import tempy from 'tempy'; +import path from 'path'; + +const temporaryDir = tempy.directory(); + +process.env.TZ = 'America/New_York'; +(process.versions as any).chrome = ''; + +export const app = { + getPath: (name: string) => path.resolve(temporaryDir, name), + isPackaged: false, + getVersion: '' +}; + +export const shell = { + showItemInFolder: sinon.fake() +}; + +export const clipboard = { + writeText: sinon.fake() +}; + +export const remote = {}; diff --git a/test/mocks/plugins.js b/test/mocks/plugins.js deleted file mode 100644 index 073cb8c7a..000000000 --- a/test/mocks/plugins.js +++ /dev/null @@ -1,5 +0,0 @@ -const sinon = require('sinon'); - -module.exports = { - getRecordingPlugins: sinon.fake() -}; diff --git a/test/mocks/plugins.ts b/test/mocks/plugins.ts new file mode 100644 index 000000000..87aab203b --- /dev/null +++ b/test/mocks/plugins.ts @@ -0,0 +1,9 @@ +import sinon from 'sinon'; +import {Mutable, PartialDeep} from 'type-fest'; +import type {Plugins} from '../../main/plugins'; + +export const plugins: PartialDeep> = { + recordingPlugins: [], + sharePlugins: [], + editPlugins: [] +}; diff --git a/test/mocks/sentry.js b/test/mocks/sentry.js deleted file mode 100644 index 74a8602a4..000000000 --- a/test/mocks/sentry.js +++ /dev/null @@ -1,6 +0,0 @@ -const sinon = require('sinon'); - -module.exports = { - captureException: sinon.fake(), - isSentryEnabled: true -}; diff --git a/test/mocks/sentry.ts b/test/mocks/sentry.ts new file mode 100644 index 000000000..3e3d6d16e --- /dev/null +++ b/test/mocks/sentry.ts @@ -0,0 +1,7 @@ +import sinon from 'sinon'; + +export const isSentryEnabled = true; + +export default { + captureException: sinon.fake() +}; diff --git a/test/mocks/service-context.js b/test/mocks/service-context.js deleted file mode 100644 index 4c42f187a..000000000 --- a/test/mocks/service-context.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -class ShareServiceContext {} -class RecordServiceContext {} -class EditServiceContext {} - -module.exports = { - ShareServiceContext, - RecordServiceContext, - EditServiceContext -}; diff --git a/test/mocks/service-context.ts b/test/mocks/service-context.ts new file mode 100644 index 000000000..7121b7dc9 --- /dev/null +++ b/test/mocks/service-context.ts @@ -0,0 +1,4 @@ + +export class ShareServiceContext {} +export class RecordServiceContext {} +export class EditServiceContext {} diff --git a/test/mocks/settings.js b/test/mocks/settings.js deleted file mode 100644 index 1e17ff1db..000000000 --- a/test/mocks/settings.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; -const sinon = require('sinon'); - -const mocks = {}; - -const mockGet = sinon.fake((key, defaultValue) => mocks[key] || defaultValue); - -module.exports = { - get: mockGet, - set: sinon.fake(), - delete: sinon.fake(), - setMock: (key, value) => { - mocks[key] = value; - } -}; diff --git a/test/mocks/settings.ts b/test/mocks/settings.ts new file mode 100644 index 000000000..4cd510b3e --- /dev/null +++ b/test/mocks/settings.ts @@ -0,0 +1,14 @@ +import sinon from 'sinon'; + +const mocks: Record = {}; + +const mockGet = sinon.fake((key: string, defaultValue: any) => mocks[key] || defaultValue); + +export const settings = { + get: mockGet, + set: sinon.fake(), + delete: sinon.fake(), + setMock: (key: string, value: any) => { + mocks[key] = value; + } +}; diff --git a/test/mocks/video.ts b/test/mocks/video.ts new file mode 100644 index 000000000..b45f0b8fe --- /dev/null +++ b/test/mocks/video.ts @@ -0,0 +1,18 @@ +import sinon from 'sinon'; + +const mocks = { + open: sinon.fake(), + constructor: sinon.fake(), + getOrCreate: sinon.fake(() => new Video()) +}; + +export default class Video { + static getOrCreate = mocks.getOrCreate; + openEditorWindow = mocks.open; + + constructor(...args: any[]) { + mocks.constructor(...args); + } + + static mocks = mocks; +} diff --git a/test/mocks/window-manager.ts b/test/mocks/window-manager.ts new file mode 100644 index 000000000..14051cc8c --- /dev/null +++ b/test/mocks/window-manager.ts @@ -0,0 +1,23 @@ +import sinon from 'sinon'; +import {SetOptional} from 'type-fest'; + +import type {WindowManager} from '../../main/windows/manager'; + +import * as dialogManager from './dialog'; + +export class MockWindowManager implements SetOptional< +WindowManager, +'setEditor' | 'setCropper' | 'setConfig' | 'setDialog' | 'setExports' | 'setPreferences' +> { + editor = { + open: sinon.fake(), + areAnyBlocking: () => false + }; + + dialog = { + open: dialogManager.showDialog, + ...dialogManager + }; +} + +export const windowManager = new MockWindowManager(); diff --git a/test/recording-history.js b/test/recording-history.ts similarity index 79% rename from test/recording-history.js rename to test/recording-history.ts index d499c5d19..231fb16fe 100644 --- a/test/recording-history.js +++ b/test/recording-history.ts @@ -1,21 +1,30 @@ -'use strict'; - -const {serial: test} = require('ava'); -const tempy = require('tempy'); -const fs = require('fs'); -const sinon = require('sinon'); -const path = require('path'); - -const {mockImport} = require('./helpers/mocks'); - -const {openEditorWindow} = mockImport('./editor', 'editor'); -const plugins = mockImport('./common/plugins', 'plugins'); -const dialog = mockImport('./dialog', 'dialog'); -const Sentry = mockImport('./utils/sentry', 'sentry'); - -const {shell} = require('electron'); - -const { +import {serial as testAny, TestInterface} from 'ava'; +import tempy from 'tempy'; +import fs from 'fs'; +import sinon, {SinonFakeTimers} from 'sinon'; +import path from 'path'; +import type {MockWindowManager} from './mocks/window-manager'; + +const test = testAny as TestInterface<{ + now: Date; + clock: SinonFakeTimers; + paths?: string[]; +}>; + +import {mockImport, mockModule} from './helpers/mocks'; + +mockImport('./windows/manager', 'window-manager'); +mockImport('./plugins', 'plugins'); +mockImport('./utils/sentry', 'sentry'); +mockImport('../common/analytics', 'analytics'); + +import {shell} from './mocks/electron'; +import * as dialog from './mocks/dialog'; +import {plugins} from './mocks/plugins'; +import Sentry from './mocks/sentry'; +import {windowManager} from './mocks/window-manager'; + +import { recordingHistory, getPastRecordings, hasActiveRecording, @@ -23,8 +32,10 @@ const { setCurrentRecording, updatePluginState, stopCurrentRecording, - cleanPastRecordings -} = require('../main/recording-history'); + cleanPastRecordings, + PastRecording +} from '../main/recording-history'; +import type {Video} from '../main/video'; const incomplete = path.resolve(__dirname, 'fixtures', 'incomplete.mp4'); const corrupt = path.resolve(__dirname, 'fixtures', 'corrupt.mp4'); @@ -35,9 +46,7 @@ test.before(t => { }); test.after(t => { - if (t.context.clock) { - t.context.clock.restore(); - } + t.context.clock.restore(); }); test.beforeEach(() => { @@ -63,8 +72,8 @@ test('`getPastRecordings()`', t => { recordingHistory.set('recordings', [{filePath: existingPath}, {filePath: missingPath}]); - t.deepEqual(getPastRecordings(), [{filePath: existingPath}]); - t.deepEqual(recordingHistory.get('recordings'), [{filePath: existingPath}]); + t.deepEqual(getPastRecordings(), [{filePath: existingPath} as PastRecording]); + t.deepEqual(recordingHistory.get('recordings'), [{filePath: existingPath} as PastRecording]); }); test('`hasActiveRecording()` with no recording', async t => { @@ -82,7 +91,7 @@ test('`hasActiveRecording()` with playable recording', async t => { recordServices: [fakeService] }; - plugins.getRecordingPlugins = sinon.fake.returns([fakePlugin]); + plugins.recordingPlugins = [fakePlugin]; recordingHistory.set('activeRecording', { filePath: incomplete, @@ -106,11 +115,11 @@ test('`hasActiveRecording()` with playable recording', async t => { // Don't delete it until user is done interacting with the dialog t.true(recordingHistory.has('activeRecording')); - await dialog.fakeAction(dialogState.buttons.findIndex(b => b.label && b.label.toLowerCase().includes('editor'))); - t.deepEqual(openEditorWindow.lastCall.args, [ - incomplete, - {recordingName: 'Incomplete'} - ]); + await dialog.fakeAction(dialogState.buttons.findIndex((b: any) => b.label?.toLowerCase().includes('editor'))); + + const video = windowManager.editor.open.lastCall?.args?.[0] as Video; + t.deepEqual(video?.filePath, incomplete); + t.deepEqual(video?.title, 'Incomplete'); t.true(await checkPromise); @@ -201,16 +210,16 @@ test('`hasActiveRecording()` with unknown corrupt recording', async t => { test('`setCurrentRecording()`', t => { setCurrentRecording({ filePath: 'some/path', - apertureOptions: {some: 'options'}, - plugins: {some: 'plugins'} + apertureOptions: {some: 'options'} as any, + plugins: {some: 'plugins'} as any }); t.deepEqual(recordingHistory.get('activeRecording'), { filePath: 'some/path', - name: 'New Recording 2020-07-21 at 11.27.26', + name: 'Kapture 2020-07-21 at 11.27.26', date: t.context.now.toISOString(), - apertureOptions: {some: 'options'}, - plugins: {some: 'plugins'} + apertureOptions: {some: 'options'} as any, + plugins: {some: 'plugins'} as any }); }); @@ -294,14 +303,14 @@ test('`cleanPastRecordings()`', t => { test('`addRecording()`', t => { const filePath = tempy.file({extension: 'mp4'}); - addRecording({filePath}); + addRecording({filePath} as PastRecording); t.is(recordingHistory.get('recordings').length, 0); fs.writeFileSync(filePath, 'data'); t.context.paths = [filePath]; - addRecording({filePath}); + addRecording({filePath} as PastRecording); - t.deepEqual(recordingHistory.get('recordings'), [{filePath}]); + t.deepEqual(recordingHistory.get('recordings'), [{filePath} as PastRecording]); }); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 000000000..5d3e32ff3 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noUnusedLocals": false, + "baseUrl": "." + }, + "include": [ + "**/*.ts" + ] +} diff --git a/testa.svg b/testa.svg new file mode 100644 index 000000000..aa4f9f80a --- /dev/null +++ b/testa.svg @@ -0,0 +1,1291 @@ + + + + + + +G + + + +common/analytics.js + +common/analytics.js + + + +common/settings.js + +common/settings.js + + + +common/analytics.js->common/settings.js + + + + + +common/constants.js + +common/constants.js + + + +common/settings.js->common/constants.js + + + + + +common/system-permissions.js + +common/system-permissions.js + + + +common/settings.js->common/system-permissions.js + + + + + +utils/devices.js + +utils/devices.js + + + +common/settings.js->utils/devices.js + + + + + +utils/shortcut-to-accelerator.js + +utils/shortcut-to-accelerator.js + + + +common/settings.js->utils/shortcut-to-accelerator.js + + + + + +common/types/index.js + +common/types/index.js + + + +common/constants.js->common/types/index.js + + + + + +common/types/base.js + +common/types/base.js + + + +common/types/index.js->common/types/base.js + + + + + +common/types/conversion-options.js + +common/types/conversion-options.js + + + +common/types/index.js->common/types/conversion-options.js + + + + + +common/types/remote-states.js + +common/types/remote-states.js + + + +common/types/index.js->common/types/remote-states.js + + + + + +common/types/window-states.js + +common/types/window-states.js + + + +common/types/index.js->common/types/window-states.js + + + + + +common/notifications.js + +common/notifications.js + + + +common/plugins.js + +common/plugins.js + + + +common/plugins.js->common/analytics.js + + + + + +common/plugins.js->common/notifications.js + + + + + +config.js + +config.js + + + +common/plugins.js->config.js + + + + + +menus.js + +menus.js + + + +common/plugins.js->menus.js + + + + + +plugin.js + +plugin.js + + + +common/plugins.js->plugin.js + + + + + +preferences.js + +preferences.js + + + +common/plugins.js->preferences.js + + + + + +utils/errors.js + +utils/errors.js + + + +common/plugins.js->utils/errors.js + + + + + +utils/sentry.js + +utils/sentry.js + + + +common/plugins.js->utils/sentry.js + + + + + +config.js->preferences.js + + + + + +utils/routes.js + +utils/routes.js + + + +config.js->utils/routes.js + + + + + +menus.js->common/settings.js + + + + + +menus.js->common/constants.js + + + + + +menus.js->preferences.js + + + + + +menus.js->common/system-permissions.js + + + + + +menus.js->utils/devices.js + + + + + +utils/dock.js + +utils/dock.js + + + +menus.js->utils/dock.js + + + + + +exports.js + +exports.js + + + +menus.js->exports.js + + + + + +cropper.js + +cropper.js + + + +menus.js->cropper.js + + + + + +editor.js + +editor.js + + + +menus.js->editor.js + + + + + +utils/open-files.js + +utils/open-files.js + + + +menus.js->utils/open-files.js + + + + + +plugin.js->utils/errors.js + + + + + +utils/plugin-config.js + +utils/plugin-config.js + + + +plugin.js->utils/plugin-config.js + + + + + +preferences.js->common/analytics.js + + + + + +preferences.js->utils/routes.js + + + + + +preferences.js->cropper.js + + + + + +utils/errors.js->utils/sentry.js + + + + + +dialog.js + +dialog.js + + + +utils/errors.js->dialog.js + + + + + +utils/sentry.js->common/settings.js + + + + + +common/system-permissions.js->utils/dock.js + + + + + +utils/devices.js->utils/errors.js + + + + + +utils/devices.js->utils/sentry.js + + + + + +utils/devices.js->common/system-permissions.js + + + + + +conversion.js + +conversion.js + + + +conversion.js->common/types/index.js + + + + + +conversion.js->utils/errors.js + + + + + +conversion.js->utils/dock.js + + + + + +converters/index.js + +converters/index.js + + + +conversion.js->converters/index.js + + + + + +export.js + +export.js + + + +conversion.js->export.js + + + + + +conversion.js->exports.js + + + + + +plugins/built-in/save-file-plugin.js + +plugins/built-in/save-file-plugin.js + + + +conversion.js->plugins/built-in/save-file-plugin.js + + + + + +plugins/index.js + +plugins/index.js + + + +conversion.js->plugins/index.js + + + + + +utils/notifications.js + +utils/notifications.js + + + +conversion.js->utils/notifications.js + + + + + +video.js + +video.js + + + +conversion.js->video.js + + + + + +converters/index.js->common/analytics.js + + + + + +converters/index.js->common/settings.js + + + + + +converters/index.js->common/constants.js + + + + + +converters/index.js->common/types/index.js + + + + + +converters/index.js->plugins/index.js + + + + + +converters/h264.js + +converters/h264.js + + + +converters/index.js->converters/h264.js + + + + + +converters/process.js + +converters/process.js + + + +converters/index.js->converters/process.js + + + + + +plugins/service-context.js + +plugins/service-context.js + + + +converters/index.js->plugins/service-context.js + + + + + +export.js->plugins/service-context.js + + + + + +menus/utils.js + +menus/utils.js + + + +export.js->menus/utils.js + + + + + +utils/formats.js + +utils/formats.js + + + +export.js->utils/formats.js + + + + + +exports.js->utils/routes.js + + + + + +plugins/built-in/save-file-plugin.js->common/settings.js + + + + + +plugins/built-in/save-file-plugin.js->common/types/index.js + + + + + +plugins/index.js->common/analytics.js + + + + + +plugins/index.js->preferences.js + + + + + +plugins/index.js->utils/errors.js + + + + + +plugins/index.js->utils/notifications.js + + + + + +plugins/plugin.js + +plugins/plugin.js + + + +plugins/index.js->plugins/plugin.js + + + + + +video.js->common/types/index.js + + + + + +video.js->editor.js + + + + + +utils/image-preview.js + +utils/image-preview.js + + + +video.js->utils/image-preview.js + + + + + +utils/timestamped-name.js + +utils/timestamped-name.js + + + +video.js->utils/timestamped-name.js + + + + + +utils/encoding.js + +utils/encoding.js + + + +video.js->utils/encoding.js + + + + + +utils/fps.js + +utils/fps.js + + + +video.js->utils/fps.js + + + + + +converters/h264.js->common/settings.js + + + + + +converters/h264.js->common/types/index.js + + + + + +converters/h264.js->converters/process.js + + + + + +converters/utils.js + +converters/utils.js + + + +converters/h264.js->converters/utils.js + + + + + +converters/process.js->common/analytics.js + + + + + +converters/process.js->common/settings.js + + + + + +converters/process.js->converters/utils.js + + + + + +plugins/service-context.js->common/constants.js + + + + + +plugins/service-context.js->utils/notifications.js + + + + + +utils/deep-linking.js + +utils/deep-linking.js + + + +plugins/service-context.js->utils/deep-linking.js + + + + + +cropper.js->common/settings.js + + + + + +cropper.js->common/system-permissions.js + + + + + +cropper.js->utils/routes.js + + + + + +cropper.js->editor.js + + + + + +kap-window.js + +kap-window.js + + + +editor.js->kap-window.js + + + + + +editor.js->menus/utils.js + + + + + +editor.js->utils/image-preview.js + + + + + +dialog.js->utils/routes.js + + + + + +kap-window.js->menus.js + + + + + +kap-window.js->utils/routes.js + + + + + +menus/application.js + +menus/application.js + + + +kap-window.js->menus/application.js + + + + + +utils/image-preview.js->utils/timestamped-name.js + + + + + +utils/formats.js->common/types/index.js + + + + + +global-accelerators.js + +global-accelerators.js + + + +global-accelerators.js->common/settings.js + + + + + +global-accelerators.js->cropper.js + + + + + +index.js + +index.js + + + +index.js->common/analytics.js + + + + + +index.js->common/settings.js + + + + + +index.js->common/plugins.js + + + + + +index.js->menus.js + + + + + +index.js->utils/errors.js + + + + + +index.js->utils/sentry.js + + + + + +index.js->common/system-permissions.js + + + + + +index.js->conversion.js + + + + + +index.js->cropper.js + + + + + +index.js->global-accelerators.js + + + + + +recording-history.js + +recording-history.js + + + +index.js->recording-history.js + + + + + +remote-states/index.js + +remote-states/index.js + + + +index.js->remote-states/index.js + + + + + +tray.js + +tray.js + + + +index.js->tray.js + + + + + +index.js->utils/deep-linking.js + + + + + +index.js->utils/open-files.js + + + + + +recording-history.js->common/plugins.js + + + + + +recording-history.js->utils/sentry.js + + + + + +recording-history.js->video.js + + + + + +recording-history.js->dialog.js + + + + + +recording-history.js->utils/timestamped-name.js + + + + + +remote-states/index.js->remote-states/index.js + + + + + +remote-states/setup-remote-state.js + +remote-states/setup-remote-state.js + + + +remote-states/index.js->remote-states/setup-remote-state.js + + + + + +tray.js->common/analytics.js + + + + + +tray.js->cropper.js + + + + + +tray.js->utils/open-files.js + + + + + +menus/cog.js + +menus/cog.js + + + +tray.js->menus/cog.js + + + + + +utils/deep-linking.js->preferences.js + + + + + +utils/open-files.js->common/constants.js + + + + + +utils/open-files.js->video.js + + + + + +menus/application.js->menus/utils.js + + + + + +menus/common.js + +menus/common.js + + + +menus/application.js->menus/common.js + + + + + +menus/common.js->common/constants.js + + + + + +menus/common.js->preferences.js + + + + + +menus/common.js->utils/dock.js + + + + + +menus/common.js->exports.js + + + + + +menus/common.js->cropper.js + + + + + +menus/common.js->menus/utils.js + + + + + +menus/common.js->utils/open-files.js + + + + + +menus/cog.js->common/settings.js + + + + + +menus/cog.js->common/constants.js + + + + + +menus/cog.js->common/system-permissions.js + + + + + +menus/cog.js->utils/devices.js + + + + + +menus/cog.js->plugins/index.js + + + + + +menus/cog.js->menus/utils.js + + + + + +menus/cog.js->menus/common.js + + + + + +utils/ajv.js + +utils/ajv.js + + + +utils/plugin-config.js->utils/ajv.js + + + + + +plugins/config.js + +plugins/config.js + + + +plugins/config.js->utils/ajv.js + + + + + +plugins/plugin.js->preferences.js + + + + + +plugins/plugin.js->utils/errors.js + + + + + +plugins/plugin.js->plugins/config.js + + + + + +remote-states/utils.js + +remote-states/utils.js + + + +remote-states/setup-remote-state.js->remote-states/utils.js + + + + + +utils/encoding.js->common/analytics.js + + + + + diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 000000000..139244d0a --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "main/**/*", + "main/**/*.js", + "test/**/*.ts", + "test/**/*.js" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..a5c72d748 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@sindresorhus/tsconfig", + "compilerOptions": { + "outDir": "dist-js", + "target": "es2019", + "module": "commonjs", + "esModuleInterop": true, + "allowJs": true, + "sourceMap": true, + "inlineSources": true, + "lib": [ + "esnext" + ] + }, + "include": [ + "node_modules/type-fest/index.d.ts", + "main/**/*" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/yarn.lock b/yarn.lock index f9d9cad80..d1cc97caf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,202 +7,159 @@ resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.0.3.tgz#bc5b5532ecafd923a61f2fb097e3b108c0106a3f" integrity sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA== -"@ampproject/toolbox-core@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.3.0.tgz#f27bd17e01fdc6725c440aefa844f63466c0f37e" - integrity sha512-NT+kVR5Rm2cxp12h40IXgPRWmq0cpUdmcgZmgdelplp/q//4aWkt2+llGHR2foQJkwICxMVVlb/XidsHz0Rh9g== - dependencies: - cross-fetch "3.0.4" - -"@ampproject/toolbox-optimizer@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.3.1.tgz#ad31d2bbeef8cf863b055bd9a520337f796a957d" - integrity sha512-614CKgpHJfVVnns1kHMuXd8uTvE0bhgIEyhnBqQ06Af1s91bKDUKDksGFVd/drGhEYmyX3Y8pizoZgw2tiQtSQ== - dependencies: - "@ampproject/toolbox-core" "^2.3.0" - "@ampproject/toolbox-runtime-version" "^2.3.1" - "@ampproject/toolbox-script-csp" "^2.3.0" - "@ampproject/toolbox-validator-rules" "^2.3.0" - cssnano "4.1.10" - domhandler "3.0.0" - domutils "2.0.0" - htmlparser2 "4.1.0" +"@ampproject/toolbox-core@2.7.4", "@ampproject/toolbox-core@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.7.4.tgz#8355136f16301458ce942acf6c55952c9a415627" + integrity sha512-qpBhcS4urB7IKc+jx2kksN7BuvvwCo7Y3IstapWo+EW+COY5EYAUwb2pil37v3TsaqHKgX//NloFP1SKzGZAnw== + dependencies: + cross-fetch "3.0.6" + lru-cache "6.0.0" + +"@ampproject/toolbox-optimizer@2.7.1-alpha.0": + version "2.7.1-alpha.0" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-optimizer/-/toolbox-optimizer-2.7.1-alpha.0.tgz#1571dcd02608223ff68f6b7223102a123e381197" + integrity sha512-WGPZKVQvHgNYJk1XVJCCmY+NVGTGJtvn0OALDyiegN4FJWOcilQUhCIcjMkZN59u1flz/u+sEKccM5qsROqVyg== + dependencies: + "@ampproject/toolbox-core" "^2.7.1-alpha.0" + "@ampproject/toolbox-runtime-version" "^2.7.1-alpha.0" + "@ampproject/toolbox-script-csp" "^2.5.4" + "@ampproject/toolbox-validator-rules" "^2.7.1-alpha.0" + abort-controller "3.0.0" + cross-fetch "3.0.6" + cssnano-simple "1.2.1" + dom-serializer "1.1.0" + domhandler "3.3.0" + domutils "2.4.2" + htmlparser2 "5.0.1" + https-proxy-agent "5.0.0" + lru-cache "6.0.0" + node-fetch "2.6.1" normalize-html-whitespace "1.0.0" + postcss "7.0.32" postcss-safe-parser "4.0.2" - terser "4.6.13" + terser "5.5.1" -"@ampproject/toolbox-runtime-version@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.3.1.tgz#c1d0d937a7474b958cb8e713ea8be00e7bc724f4" - integrity sha512-ocDCSaSUlbgPbuXRVyLU7k+dLGYluKrnfSPKXIwwpGkwNw58JOddvgnQGyB0R/2Ma36nxFA8AAyxjnghLSScpg== +"@ampproject/toolbox-runtime-version@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-runtime-version/-/toolbox-runtime-version-2.7.4.tgz#f49da0dab122101ef75ed3caed3a0142487b73e1" + integrity sha512-SAdOUOERp42thVNWaBJlnFvFVvnacMVnz5z9LyUZHSnoL1EqrAW5Sz5jv+Ly+gkA8NYsEaUxAdSCBAzE9Uzb4Q== dependencies: - "@ampproject/toolbox-core" "^2.3.0" + "@ampproject/toolbox-core" "2.7.4" -"@ampproject/toolbox-script-csp@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-2.3.0.tgz#374cd0bf69bfdd0f1784064d0de69162722c89af" - integrity sha512-Qba53ohvCH79sYl5O8K5GMSo/372OjuyxNc+XySG26sAsG26WpBKJEE0HTr8rsa//CD3Fc92FieT1gK5U/jK4Q== +"@ampproject/toolbox-script-csp@^2.5.4": + version "2.5.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-script-csp/-/toolbox-script-csp-2.5.4.tgz#d8b7b91a678ae8f263cb36d9b74e441b7d633aad" + integrity sha512-+knTYetI5nWllRZ9wFcj7mYxelkiiFVRAAW/hl0ad8EnKHMH82tRlk40CapEnUHhp6Er5sCYkumQ8dngs3Q4zQ== -"@ampproject/toolbox-validator-rules@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.3.0.tgz#047d8a8106ba777f1df308c19f1c1c41ffea4054" - integrity sha512-S10YIyOKettoRDWoyRymRyjzWZD4/qW7YfHNhHAS13QVneabRcU5MF7vEwkG6dHWx/UdufT5GbqYnvpQRMNt3Q== +"@ampproject/toolbox-validator-rules@^2.7.1-alpha.0": + version "2.7.4" + resolved "https://registry.yarnpkg.com/@ampproject/toolbox-validator-rules/-/toolbox-validator-rules-2.7.4.tgz#a58b5eca723f6c3557ac83b696de0247f5f03ce4" + integrity sha512-z3JRcpIZLLdVC9XVe7YTZuB3a/eR9s2SjElYB9AWRdyJyL5Jt7+pGNv4Uwh1uHVoBXsWEVQzNOWSNtrO3mSwZA== dependencies: - cross-fetch "3.0.4" + cross-fetch "3.0.6" -"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/code-frame@^7.10.4": +"@babel/code-frame@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.6.tgz#3f604c40e420131affe6f2c8052e9a275ae2049b" - integrity sha512-5QPTrNen2bm7RBc7dsOmcA5hbrS4O2Vhmk5XOL4zWW/zD/hV0iinpefDlkm+tBBy8kDtFaaeEvmAqt+nURAV2g== +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: - browserslist "^4.11.1" - invariant "^2.2.4" - semver "^5.5.0" + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" -"@babel/core@7.7.7": - version "7.7.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.7.7.tgz#ee155d2e12300bcc0cff6a8ad46f2af5063803e9" - integrity sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.7.7" - "@babel/helpers" "^7.7.4" - "@babel/parser" "^7.7.7" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@babel/types" "^7.7.4" +"@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/compat-data@^7.13.8": + version "7.13.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6" + integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog== + +"@babel/core@^7.12.16": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" + integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.10" + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.10" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" convert-source-map "^1.7.0" debug "^4.1.0" - json5 "^2.1.0" - lodash "^4.17.13" - resolve "^1.3.2" - semver "^5.4.1" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.10.5", "@babel/generator@^7.9.0": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" - integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== +"@babel/eslint-parser@^7.12.16": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.13.10.tgz#e272979914f36bb6cea144c14c32bb51632696dd" + integrity sha512-/I1HQ3jGPhIpeBFeI3wO9WwWOnBYpuR0pX0KlkdGcRQAVX9prB/FCS2HBpL7BiFbzhny1YCiBH8MTZD2jJa7Hg== dependencies: - "@babel/types" "^7.10.5" - jsesc "^2.5.1" - source-map "^0.5.0" + eslint-scope "5.1.0" + eslint-visitor-keys "^1.3.0" + semver "^6.3.0" -"@babel/generator@^7.7.7", "@babel/generator@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43" - integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ== +"@babel/generator@^7.13.0", "@babel/generator@^7.13.9": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== dependencies: - "@babel/types" "^7.9.6" + "@babel/types" "^7.13.0" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" - integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" - integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-builder-react-jsx-experimental@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz#066d80262ade488f9c1b1823ce5db88a4cedaa43" - integrity sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-module-imports" "^7.8.3" - "@babel/types" "^7.9.0" - -"@babel/helper-builder-react-jsx@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32" - integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/types" "^7.9.0" - -"@babel/helper-compilation-targets@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.9.6.tgz#1e05b7ccc9d38d2f8b40b458b380a04dcfadd38a" - integrity sha512-x2Nvu0igO0ejXzx09B/1fGBxY9NXQlBW2kZsSxCJft+KHN8t9XWzIvFxtPHnBOAXpVsdxZKZFbRUC8TsNKajMw== - dependencies: - "@babel/compat-data" "^7.9.6" - browserslist "^4.11.1" - invariant "^2.2.4" - levenary "^1.1.1" - semver "^5.5.0" - -"@babel/helper-create-class-features-plugin@^7.8.3", "@babel/helper-create-class-features-plugin@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.9.6.tgz#965c8b0a9f051801fd9d3b372ca0ccf200a90897" - integrity sha512-6N9IeuyHvMBRyjNYOMJHrhwtu4WJMrYf8hVbEHD3pbbbmNOk1kmXSQs7bA4dYDUaIx4ZEzdnvo6NwC3WHd/Qow== - dependencies: - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.9.6" - "@babel/helper-split-export-declaration" "^7.8.3" - -"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087" - integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - regexpu-core "^4.7.0" - -"@babel/helper-define-map@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" - integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== +"@babel/generator@^7.9.0": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.10.5.tgz#1b903554bc8c583ee8d25f1e8969732e6b829a69" + integrity sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig== dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/types" "^7.8.3" - lodash "^4.17.13" + "@babel/types" "^7.10.5" + jsesc "^2.5.1" + source-map "^0.5.0" -"@babel/helper-explode-assignable-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" - integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== +"@babel/helper-compilation-targets@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" + integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== dependencies: - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/compat-data" "^7.13.8" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.14.5" + semver "^6.3.0" -"@babel/helper-function-name@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" - integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== +"@babel/helper-function-name@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" + integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== dependencies: - "@babel/helper-get-function-arity" "^7.10.4" - "@babel/template" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/helper-function-name@^7.8.3", "@babel/helper-function-name@^7.9.5": +"@babel/helper-function-name@^7.8.3": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== @@ -211,12 +168,12 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.9.5" -"@babel/helper-get-function-arity@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" - integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== dependencies: - "@babel/types" "^7.10.4" + "@babel/types" "^7.12.13" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -225,118 +182,65 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-hoist-variables@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" - integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-member-expression-to-functions@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz#172f56e7a63e78112f3a04055f24365af702e7ee" - integrity sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA== - dependencies: - "@babel/types" "^7.10.5" - -"@babel/helper-member-expression-to-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" - integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-imports@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" - integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-module-transforms@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" - integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-simple-access" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/template" "^7.8.6" - "@babel/types" "^7.9.0" - lodash "^4.17.13" - -"@babel/helper-optimise-call-expression@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" - integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg== - dependencies: - "@babel/types" "^7.10.4" - -"@babel/helper-optimise-call-expression@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" - integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== - dependencies: - "@babel/types" "^7.8.3" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" - integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-member-expression-to-functions@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091" + integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== + dependencies: + "@babel/types" "^7.13.0" + +"@babel/helper-module-imports@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" + integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-module-transforms@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" + integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== + dependencies: + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-replace-supers" "^7.13.0" + "@babel/helper-simple-access" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/helper-validator-identifier" "^7.12.11" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + lodash "^4.17.19" -"@babel/helper-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" - integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== +"@babel/helper-optimise-call-expression@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" + integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== dependencies: - lodash "^4.17.13" + "@babel/types" "^7.12.13" -"@babel/helper-remap-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" - integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== +"@babel/helper-replace-supers@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24" + integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-wrap-function" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-member-expression-to-functions" "^7.13.0" + "@babel/helper-optimise-call-expression" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" -"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" - integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== +"@babel/helper-simple-access@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" + integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== dependencies: - "@babel/helper-member-expression-to-functions" "^7.8.3" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/types" "^7.12.13" -"@babel/helper-replace-supers@^7.9.6": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" - integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A== +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.10.4" - "@babel/helper-optimise-call-expression" "^7.10.4" - "@babel/traverse" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/helper-simple-access@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" - integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== - dependencies: - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" - -"@babel/helper-split-export-declaration@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz#2c70576eaa3b5609b24cb99db2888cc3fc4251d1" - integrity sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg== - dependencies: - "@babel/types" "^7.10.4" + "@babel/types" "^7.12.13" "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" @@ -350,29 +254,29 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + "@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helper-wrap-function@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" - integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== - dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.3" - "@babel/types" "^7.8.3" +"@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== -"@babel/helpers@^7.7.4": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580" - integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw== +"@babel/helpers@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" + integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== dependencies: - "@babel/template" "^7.8.3" - "@babel/traverse" "^7.9.6" - "@babel/types" "^7.9.6" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" "@babel/highlight@^7.10.4": version "7.10.4" @@ -383,6 +287,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.12.13": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" + integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/highlight@^7.8.3": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" @@ -390,623 +303,47 @@ dependencies: "@babel/helper-validator-identifier" "^7.9.0" chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.9.0": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" - integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== - -"@babel/parser@^7.7.0", "@babel/parser@^7.8.6": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" - integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== - -"@babel/parser@^7.7.7", "@babel/parser@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7" - integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q== - -"@babel/plugin-proposal-async-generator-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" - integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" - "@babel/plugin-syntax-async-generators" "^7.8.0" - -"@babel/plugin-proposal-class-properties@7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz#5e06654af5cd04b608915aada9b2a6788004464e" - integrity sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-proposal-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" - integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - -"@babel/plugin-proposal-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" - integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.0" - -"@babel/plugin-proposal-nullish-coalescing-operator@7.8.3", "@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" - integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - -"@babel/plugin-proposal-numeric-separator@7.8.3", "@babel/plugin-proposal-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8" - integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - -"@babel/plugin-proposal-object-rest-spread@7.9.6", "@babel/plugin-proposal-object-rest-spread@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.6.tgz#7a093586fcb18b08266eb1a7177da671ac575b63" - integrity sha512-Ga6/fhGqA9Hj+y6whNpPv8psyaK5xzrQwSPsGPloVkvmH+PqW1ixdnfJ9uIO06OjQNYol3PMnfmJ8vfZtkzF+A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-transform-parameters" "^7.9.5" - -"@babel/plugin-proposal-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" - integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - -"@babel/plugin-proposal-optional-chaining@7.9.0", "@babel/plugin-proposal-optional-chaining@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58" - integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - -"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d" - integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.8" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-async-generators@^7.8.0": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-dynamic-import@7.8.3", "@babel/plugin-syntax-dynamic-import@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-json-strings@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94" - integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" - integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-object-rest-spread@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" - integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-typescript@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz#c1f659dda97711a569cef75275f7e15dcaa6cabc" - integrity sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-arrow-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" - integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-async-to-generator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" - integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-remap-async-to-generator" "^7.8.3" - -"@babel/plugin-transform-block-scoped-functions@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" - integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-block-scoping@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" - integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - lodash "^4.17.13" - -"@babel/plugin-transform-classes@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.5.tgz#800597ddb8aefc2c293ed27459c1fcc935a26c2c" - integrity sha512-x2kZoIuLC//O5iA7PEvecB105o7TLzZo8ofBVhP79N+DO3jaX+KYfww9TQcfBEZD0nikNyYcGB1IKtRq36rdmg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-define-map" "^7.8.3" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-optimise-call-expression" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.6" - "@babel/helper-split-export-declaration" "^7.8.3" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" - integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-destructuring@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.9.5.tgz#72c97cf5f38604aea3abf3b935b0e17b1db76a50" - integrity sha512-j3OEsGel8nHL/iusv/mRd5fYZ3DrOxWC82x0ogmdN/vHfAP4MYw+AFKYanzWlktNwikKvlzUV//afBW5FTp17Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" - integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-duplicate-keys@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" - integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-exponentiation-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" - integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-for-of@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e" - integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" - integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== - dependencies: - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" - integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-member-expression-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" - integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-modules-amd@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.6.tgz#8539ec42c153d12ea3836e0e3ac30d5aae7b258e" - integrity sha512-zoT0kgC3EixAyIAU+9vfaUVKTv9IxBDSabgHoUCBP6FqEJ+iNiN7ip7NBKcYqbfUDfuC2mFCbM7vbu4qJgOnDw== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@7.9.6", "@babel/plugin-transform-modules-commonjs@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.6.tgz#64b7474a4279ee588cacd1906695ca721687c277" - integrity sha512-7H25fSlLcn+iYimmsNe3uK1at79IE6SKW9q0/QeEHTMC9MdOZ+4bA+T1VFB5fgOqBWoqlifXRzYD0JPdmIrgSQ== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-simple-access" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.6.tgz#207f1461c78a231d5337a92140e52422510d81a4" - integrity sha512-NW5XQuW3N2tTHim8e1b7qGy7s0kZ2OH3m5octc49K1SdAKGxYxeIx7hiIz05kS1R2R+hOWcsr1eYwcGhrdHsrg== - dependencies: - "@babel/helper-hoist-variables" "^7.8.3" - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697" - integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ== - dependencies: - "@babel/helper-module-transforms" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" - integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - -"@babel/plugin-transform-new-target@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" - integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-object-super@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" - integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-replace-supers" "^7.8.3" - -"@babel/plugin-transform-parameters@^7.9.5": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.5.tgz#173b265746f5e15b2afe527eeda65b73623a0795" - integrity sha512-0+1FhHnMfj6lIIhVvS4KGQJeuhe1GI//h5uptK4PvLt+BGBxsoUJbd3/IW002yk//6sZPlFgsG1hY6OHLcy6kA== - dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-property-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" - integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-react-display-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz#70ded987c91609f78353dd76d2fb2a0bb991e8e5" - integrity sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-react-jsx-development@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz#3c2a130727caf00c2a293f0aed24520825dbf754" - integrity sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw== - dependencies: - "@babel/helper-builder-react-jsx-experimental" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" - -"@babel/plugin-transform-react-jsx-self@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz#f4f26a325820205239bb915bad8e06fcadabb49b" - integrity sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" - -"@babel/plugin-transform-react-jsx-source@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz#89ef93025240dd5d17d3122294a093e5e0183de0" - integrity sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" - -"@babel/plugin-transform-react-jsx@^7.9.4": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.4.tgz#86f576c8540bd06d0e95e0b61ea76d55f6cbd03f" - integrity sha512-Mjqf3pZBNLt854CK0C/kRuXAnE6H/bo7xYojP+WGtX8glDGSibcwnsWwhwoSuRg0+EBnxPC1ouVnuetUIlPSAw== - dependencies: - "@babel/helper-builder-react-jsx" "^7.9.0" - "@babel/helper-builder-react-jsx-experimental" "^7.9.0" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" - -"@babel/plugin-transform-regenerator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8" - integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" - integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.6.tgz#3ba804438ad0d880a17bca5eaa0cdf1edeedb2fd" - integrity sha512-qcmiECD0mYOjOIt8YHNsAP1SxPooC/rDmfmiSK9BNY72EitdSc7l44WTEklaWuFtbOEBjNhWWyph/kOImbNJ4w== - dependencies: - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - resolve "^1.8.1" - semver "^5.5.1" - -"@babel/plugin-transform-shorthand-properties@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" - integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" - integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-sticky-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" - integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/helper-regex" "^7.8.3" - -"@babel/plugin-transform-template-literals@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" - integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-typeof-symbol@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412" - integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-transform-typescript@^7.9.0": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.6.tgz#2248971416a506fc78278fc0c0ea3179224af1e9" - integrity sha512-8OvsRdvpt3Iesf2qsAn+YdlwAJD7zJ+vhFZmDCa4b8dTp7MmHtKk5FF2mCsGxjZwuwsy/yIIay/nLmxST1ctVQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.9.6" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-syntax-typescript" "^7.8.3" - -"@babel/plugin-transform-unicode-regex@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" - integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/preset-env@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.6.tgz#df063b276c6455ec6fcfc6e53aacc38da9b0aea6" - integrity sha512-0gQJ9RTzO0heXOhzftog+a/WyOuqMrAIugVYxMYf83gh1CQaQDjMtsOpqOwXyDL/5JcWsrCm8l4ju8QC97O7EQ== - dependencies: - "@babel/compat-data" "^7.9.6" - "@babel/helper-compilation-targets" "^7.9.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-proposal-async-generator-functions" "^7.8.3" - "@babel/plugin-proposal-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-json-strings" "^7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator" "^7.8.3" - "@babel/plugin-proposal-object-rest-spread" "^7.9.6" - "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining" "^7.9.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.8.0" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - "@babel/plugin-transform-arrow-functions" "^7.8.3" - "@babel/plugin-transform-async-to-generator" "^7.8.3" - "@babel/plugin-transform-block-scoped-functions" "^7.8.3" - "@babel/plugin-transform-block-scoping" "^7.8.3" - "@babel/plugin-transform-classes" "^7.9.5" - "@babel/plugin-transform-computed-properties" "^7.8.3" - "@babel/plugin-transform-destructuring" "^7.9.5" - "@babel/plugin-transform-dotall-regex" "^7.8.3" - "@babel/plugin-transform-duplicate-keys" "^7.8.3" - "@babel/plugin-transform-exponentiation-operator" "^7.8.3" - "@babel/plugin-transform-for-of" "^7.9.0" - "@babel/plugin-transform-function-name" "^7.8.3" - "@babel/plugin-transform-literals" "^7.8.3" - "@babel/plugin-transform-member-expression-literals" "^7.8.3" - "@babel/plugin-transform-modules-amd" "^7.9.6" - "@babel/plugin-transform-modules-commonjs" "^7.9.6" - "@babel/plugin-transform-modules-systemjs" "^7.9.6" - "@babel/plugin-transform-modules-umd" "^7.9.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" - "@babel/plugin-transform-new-target" "^7.8.3" - "@babel/plugin-transform-object-super" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.9.5" - "@babel/plugin-transform-property-literals" "^7.8.3" - "@babel/plugin-transform-regenerator" "^7.8.7" - "@babel/plugin-transform-reserved-words" "^7.8.3" - "@babel/plugin-transform-shorthand-properties" "^7.8.3" - "@babel/plugin-transform-spread" "^7.8.3" - "@babel/plugin-transform-sticky-regex" "^7.8.3" - "@babel/plugin-transform-template-literals" "^7.8.3" - "@babel/plugin-transform-typeof-symbol" "^7.8.4" - "@babel/plugin-transform-unicode-regex" "^7.8.3" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.9.6" - browserslist "^4.11.1" - core-js-compat "^3.6.2" - invariant "^2.2.2" - levenary "^1.1.1" - semver "^5.5.0" - -"@babel/preset-modules@0.1.3", "@babel/preset-modules@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" - integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" + js-tokens "^4.0.0" -"@babel/preset-react@7.9.4": - version "7.9.4" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.9.4.tgz#c6c97693ac65b6b9c0b4f25b948a8f665463014d" - integrity sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-react-display-name" "^7.8.3" - "@babel/plugin-transform-react-jsx" "^7.9.4" - "@babel/plugin-transform-react-jsx-development" "^7.9.0" - "@babel/plugin-transform-react-jsx-self" "^7.9.0" - "@babel/plugin-transform-react-jsx-source" "^7.9.0" +"@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.10.tgz#8f8f9bf7b3afa3eabd061f7a5bcdf4fec3c48409" + integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ== -"@babel/preset-typescript@7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz#87705a72b1f0d59df21c179f7c3d2ef4b16ce192" - integrity sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - "@babel/plugin-transform-typescript" "^7.9.0" +"@babel/parser@^7.7.0", "@babel/parser@^7.8.6": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" + integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== -"@babel/runtime-corejs3@^7.8.3": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.9.6.tgz#67aded13fffbbc2cb93247388cf84d77a4be9a71" - integrity sha512-6toWAfaALQjt3KMZQc6fABqZwUDDuWzz+cAfPhqyEnzxvdWOAkjwPNxgF8xlmo7OWLsSjaKjsskpKHRLaMArOA== - dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.4" +"@babel/parser@^7.9.0": + version "7.10.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.5.tgz#e7c6bf5a7deff957cec9f04b551e2762909d826b" + integrity sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ== -"@babel/runtime@7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" - integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== +"@babel/runtime@7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.8.7": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== +"@babel/template@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" -"@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": +"@babel/template@^7.8.3": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== @@ -1015,22 +352,22 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/traverse@^7.10.4": - version "7.10.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.10.5.tgz#77ce464f5b258be265af618d8fddf0536f20b564" - integrity sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.10.5" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.10.4" - "@babel/parser" "^7.10.5" - "@babel/types" "^7.10.5" +"@babel/traverse@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" + integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.0" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.0" + "@babel/types" "^7.13.0" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.19" -"@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.7.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== @@ -1045,21 +382,6 @@ globals "^11.1.0" lodash "^4.17.13" -"@babel/traverse@^7.7.4", "@babel/traverse@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442" - integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.6" - "@babel/helper-function-name" "^7.9.5" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.9.6" - "@babel/types" "^7.9.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - "@babel/types@7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" @@ -1069,16 +391,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@7.9.6", "@babel/types@^7.7.4", "@babel/types@^7.9.5", "@babel/types@^7.9.6": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" - integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== - dependencies: - "@babel/helper-validator-identifier" "^7.9.5" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.10.4", "@babel/types@^7.10.5": +"@babel/types@^7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.10.5.tgz#d88ae7e2fde86bfbfe851d4d81afa70a997b5d15" integrity sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q== @@ -1087,7 +400,16 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": +"@babel/types@^7.12.13", "@babel/types@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" + integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== @@ -1096,6 +418,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.9.5": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" + integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@concordance/react@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@concordance/react/-/react-2.0.0.tgz#aef913f27474c53731f4fd79cc2f54897de90fde" @@ -1127,6 +458,22 @@ global-agent "^2.0.2" global-tunnel-ng "^2.7.1" +"@eslint/eslintrc@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.20" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@ffmpeg-installer/darwin-x64@4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz#48e1706c690e628148482bfb64acb67472089aaa" @@ -1175,6 +522,26 @@ resolved "https://registry.yarnpkg.com/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz#17e8699b5798d4c60e36e2d6326a8ebe5e95a2c5" integrity sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg== +"@hapi/accept@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.1.tgz#068553e867f0f63225a506ed74e899441af53e10" + integrity sha512-fMr4d7zLzsAXo28PRRQPXR1o2Wmu+6z+VY1UzDp0iFo13Twj8WePakwXBiqn3E1aAlTpSNzCXdnnQXFhst8h8Q== + dependencies: + "@hapi/boom" "9.x.x" + "@hapi/hoek" "9.x.x" + +"@hapi/boom@9.x.x": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.0.tgz#0d9517657a56ff1e0b42d0aca9da1b37706fec56" + integrity sha512-4nZmpp4tXbm162LaZT45P7F7sgiem8dwAh2vHWT6XX24dozNjGMg6BvKCRvtCUcmcXqeMIUqWN8Rc5X8yKuROQ== + dependencies: + "@hapi/hoek" "9.x.x" + +"@hapi/hoek@9.x.x": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.0.tgz#6c9eafc78c1529248f8f4d92b0799a712b6052c6" + integrity sha512-i9YbZPN3QgfighY/1X1Pu118VUz2Fmmhd6b2n0/O8YVgGGfw0FbUYoA97k7FkpGJ+pLCFEDLUmAPPV4D1kpeFw== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1183,12 +550,22 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@next/react-dev-overlay@9.4.2": - version "9.4.2" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-9.4.2.tgz#b8d816ae1bbf89cd6f3e509fa1fd8e4dfed7f8fd" - integrity sha512-Dg6ysRiGIu6wdJ3iOcBEJY5MZwmd3CSPawSpQsj+rClNwcHqLKJKz3TfMTKH05r77On68GNKq4RaYix8y0D3Qw== +"@next/env@10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-10.0.4.tgz#ac759094d021853616af56a7bd6720e44a92a303" + integrity sha512-U+XIL75XM1pCmY4+9kYbst/0IptlfDnkFfKdgADBZulQlfng4RB3zirdzkoBtod0lVcrGgDryzOi1mM23RiiVQ== + +"@next/polyfill-module@10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-10.0.4.tgz#c34391a12ad80d6e373c403f96c8e2bbd793dca1" + integrity sha512-i2gLUa3YuZ2eQg+d91n+jS4YbPVKg1v0HHIUeJFJMMtpG/apBkTuTLBQGJXe4nKNf7/41NWLDft4ihC3Zfd+Yw== + +"@next/react-dev-overlay@10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-10.0.4.tgz#c578a3c71e2f8a8fe2aae8007cc40d1cf10bc768" + integrity sha512-8pKN0PspEtfVFqeSpNQymfXWyV95OTIT0xP9IqILJX2+52ICdU5D+YNuNIwpc4ZOZ0CssM/uYsz6K1FHbCaU7A== dependencies: - "@babel/code-frame" "7.8.3" + "@babel/code-frame" "7.10.4" ally.js "1.4.1" anser "1.4.9" chalk "4.0.0" @@ -1199,10 +576,10 @@ stacktrace-parser "0.1.10" strip-ansi "6.0.0" -"@next/react-refresh-utils@9.4.2": - version "9.4.2" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-9.4.2.tgz#a94c793b8442bcad29d4413740955dca5d7c555d" - integrity sha512-N2bn3fU+N26Pkf+c7ZaG04T9FrDUeGtIBaJ/NGygm9GMrIkCU/6nkeWNboaAiHvji8UooqhLf1Yesf4BJLzHOA== +"@next/react-refresh-utils@10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-10.0.4.tgz#5ad753572891aa7cb1010b358cc4241d7be20d20" + integrity sha512-kZ/37aSQaR0GCZVqh7WDLkeEZqzjPQoZUDdo6TOWiIEb+089AmfYp8A4/1ra9Fu8T4b4wnB76TRl6tp6DeJLXg== "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -1230,6 +607,18 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@opentelemetry/api@0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.14.0.tgz#4e17d8d2f1da72b19374efa7b6526aa001267cae" + integrity sha512-L7RMuZr5LzMmZiQSQDy9O1jo0q+DaLy6XpYJfIGfYSfoJA5qzYwUP3sP1uMIQ549DvxAgM3ng85EaPTM/hUHwQ== + dependencies: + "@opentelemetry/context-base" "^0.14.0" + +"@opentelemetry/context-base@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.14.0.tgz#c67fc20a4d891447ca1a855d7d70fa79a3533001" + integrity sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw== + "@sentry/apm@5.13.2": version "5.13.2" resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.13.2.tgz#3a0809912426f52e19b1f4a603e99423a0ac8fb9" @@ -1376,6 +765,11 @@ "@sentry/types" "5.15.5" tslib "^1.9.3" +"@sindresorhus/fnv1a@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/fnv1a/-/fnv1a-1.2.0.tgz#d554da64c406f3b62ad06dfce9efd537a4a55de4" + integrity sha512-5ezb/dBSTWtKQ4sLQwMgOJyREXJcZZkTMbendMwKrXTghUhWjZhstzkkmt4/WkFy/GSTSGzfJOKU7dEXv3C/XQ== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -1386,11 +780,23 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/string-hash@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/string-hash/-/string-hash-1.2.0.tgz#20d7f88484406f2992391b55cf8aa486103f4d03" + integrity sha512-DLUEF2sS37k9QwG5mVP36suIVJO0vmXRvUCJT7s17AbMxCJMoyV/X5nAy3sEgB84MkHJd2WmEMtCSfmhZNk46A== + dependencies: + "@sindresorhus/fnv1a" "^1.2.0" + "@sindresorhus/to-milliseconds@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@sindresorhus/to-milliseconds/-/to-milliseconds-1.2.0.tgz#3453e58b9cc973ccc98fe571dbb7a1d9737f06e7" integrity sha512-nHpLEF6oRZJZ0ym8hmxz4jeSdnOqwWd5GC75GNQqNjfSG1IY55RE3AaGEC/QUDElLTuaPSBVa1rnV/C/rUkAUw== +"@sindresorhus/tsconfig@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/tsconfig/-/tsconfig-0.7.0.tgz#8388e864b5e6d72547175ed98152b736df39b20a" + integrity sha512-on7+0FFpUH7+g5iLFkmk0oruqzE6uUnpKK0D6HM637aIvQ/mlXz33VlamsS4TSU6pifWSNIrUxrNKjAReU2Juw== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": version "1.8.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d" @@ -1434,21 +840,30 @@ dependencies: defer-to-connect "^1.0.1" -"@types/color-name@^1.1.1": +"@types/color-convert@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" + integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ== + dependencies: + "@types/color-name" "*" + +"@types/color-name@*", "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/color@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.1.tgz#2900490ed04da8116c5058cd5dba3572d5a25071" + integrity sha512-oeUWVaAwI+xINDUx+3F2vJkl/vVB03VChFF/Gl3iQCdbcakjuoJyMOba+3BXRtnBhxZ7uBYqQBi9EpLnvSoztA== + dependencies: + "@types/color-convert" "*" + "@types/debug@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -1470,40 +885,96 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/got@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.0.tgz#bba73ff0a46b2691ddb3d86e89f8b2f43ebb829d" + integrity sha512-t00QebxBllcawe7uMxi9jsfWfbki4+/zpJEPyoGWjUvnFOy4EkAzoYOI2UJt2+Fx0yIcDX2+us93k6ensKTddA== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + +"@types/insight@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@types/insight/-/insight-0.8.0.tgz#12baa0119597cb2bc2bdafc35c7aebd9c6598189" + integrity sha512-yJ6QOQm2SYTo/fqAEjDgIOt6YWlF9+2d6/sLVeJ0zmBVq8xDTlTk7ylWWDr6REQc+1aRzZbrFClXo1KsCeMyBA== + "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/lodash@^4.14.168": + version "4.14.168" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" + integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== + "@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/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/module-alias@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.0.tgz#882668f8b8cdbda44812c3b592c590909e18849e" + integrity sha512-e3sW4oEH0qS1QxSfX7PT6xIi5qk/YSMsrB9Lq8EtkhQBZB+bKyfkP+jpLJRySanvBhAQPSv2PEBe81M8Iy/7yg== + "@types/node@*": version "13.9.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.8.tgz#09976420fc80a7a00bf40680c63815ed8c7616f4" integrity sha512-1WgO8hsyHynlx7nhP1kr0OFzsgKz5XDQL+Lfc3b1Q3qIln/n8cKD4m09NJ0+P1Rq7Zgnc7N0+SsMnoD1rEb0kA== "@types/node@^12.0.12": - version "12.12.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.34.tgz#0a5d6ae5d22612f0cf5f10320e1fc5d2a745dcb8" - integrity sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g== + version "12.20.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.5.tgz#4ca82a766f05c359fd6c77505007e5a272f4bb9b" + integrity sha512-5Oy7tYZnu3a4pnJ//d4yVvOImExl4Vtwf0D40iKUlU+XlUsyV9iyFWyCFlwy489b72FMAik/EFwRkNLjjOdSPg== + +"@types/node@^14.11.10": + version "14.11.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.10.tgz#8c102aba13bf5253f35146affbf8b26275069bef" + integrity sha512-yV1nWZPlMFpoXyoknm4S56y2nlTAuFYaJuQtYRAOU7xA/FJ9RY0Xm7QOkaYMMmr8ESdHIuUb6oQgR/0+2NqlyA== "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/object-hash@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/q@^1.5.1": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" - integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@^16.9.46": + version "16.9.46" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.46.tgz#f0326cd7adceda74148baa9bff6e918632f5069e" + integrity sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" "@types/semver@^7.1.0": version "7.2.0" @@ -1512,6 +983,23 @@ dependencies: "@types/node" "*" +"@types/sinon@^9.0.10": + version "9.0.10" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.10.tgz#7fb9bcb6794262482859cab66d59132fca18fcf7" + integrity sha512-/faDC0erR06wMdybwI/uR8wEKV/E83T0k4sepIpB7gXuy2gzx2xiOjmztq6a2Y6rIGJ04D+6UU0VBmWy+4HEMA== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + +"@types/tough-cookie@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" + integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== + "@types/yargs-parser@*": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" @@ -1524,49 +1012,76 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^2.29.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9" - integrity sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ== +"@typescript-eslint/eslint-plugin@^4.11.1", "@typescript-eslint/eslint-plugin@^4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz#13a5a07cf30d0d5781e43480aa2a8d38d308b084" + integrity sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ== dependencies: - "@typescript-eslint/experimental-utils" "2.34.0" + "@typescript-eslint/experimental-utils" "4.15.0" + "@typescript-eslint/scope-manager" "4.15.0" + debug "^4.1.1" functional-red-black-tree "^1.0.1" + lodash "^4.17.15" regexpp "^3.0.0" + semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== +"@typescript-eslint/experimental-utils@4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz#b87c36410a9b23f637689427be85007a2ec1a9c6" + integrity sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" + "@typescript-eslint/scope-manager" "4.15.0" + "@typescript-eslint/types" "4.15.0" + "@typescript-eslint/typescript-estree" "4.15.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^2.29.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.34.0.tgz#50252630ca319685420e9a39ca05fe185a256bc8" - integrity sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA== +"@typescript-eslint/parser@^4.11.1", "@typescript-eslint/parser@^4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.15.0.tgz#8df94365b4b7161f9e8514fe28aef19954810b6b" + integrity sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg== dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.34.0" - "@typescript-eslint/typescript-estree" "2.34.0" - eslint-visitor-keys "^1.1.0" + "@typescript-eslint/scope-manager" "4.15.0" + "@typescript-eslint/types" "4.15.0" + "@typescript-eslint/typescript-estree" "4.15.0" + debug "^4.1.1" -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== +"@typescript-eslint/scope-manager@4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz#c42703558ea6daaaba51a9c3a86f2902dbab9432" + integrity sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g== dependencies: + "@typescript-eslint/types" "4.15.0" + "@typescript-eslint/visitor-keys" "4.15.0" + +"@typescript-eslint/types@4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.15.0.tgz#3011ae1ac3299bb9a5ac56bdd297cccf679d3662" + integrity sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg== + +"@typescript-eslint/typescript-estree@4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz#402c86a7d2111c1f7a2513022f22a38a395b7f93" + integrity sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA== + dependencies: + "@typescript-eslint/types" "4.15.0" + "@typescript-eslint/visitor-keys" "4.15.0" debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" + globby "^11.0.1" is-glob "^4.0.1" - lodash "^4.17.15" semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/visitor-keys@4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz#2a07768df30c8a5673f1bce406338a07fdec38ca" + integrity sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA== + dependencies: + "@typescript-eslint/types" "4.15.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -1722,47 +1237,63 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + acorn-jsx@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-jsx@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn-walk@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.2.tgz#d4632bfc63fd93d0f15fd05ea0e984ffd3f5a8c3" + integrity sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A== acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" - integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== +acorn@^8.0.4: + version "8.0.5" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.5.tgz#a3bfb872a74a6a7f661bc81b9849d9cac12601b7" + integrity sha512-v+DieK/HJkJOpFBETDJioequtc3PfxsWMaxIdIwujtF7FEV/MAyDQLlm6/zPvr7Mix07mLh6ccVwIsloceodlg== -adjust-sourcemap-loader@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" - integrity sha512-4hFsTsn58+YjrU9qKzML2JSSDqKvN8mUGQ0nNIrfPi8hmIONT4L3uUaT6MKdMsZ9AjsU6D2xDkZxCkbQPxChrA== +adjust-sourcemap-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" + integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== dependencies: - assert "1.4.1" - camelcase "5.0.0" - loader-utils "1.2.3" - object-path "0.11.4" - regex-parser "2.2.10" + loader-utils "^2.0.0" + regex-parser "^2.2.11" agent-base@5: version "5.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== +agent-base@6: + version "6.0.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" + integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== + dependencies: + debug "4" + aggregate-error@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" @@ -1784,11 +1315,23 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-1.5.1.tgz#0f301b1b3846182f224cc563fc0a032daafb7dab" + integrity sha512-s1RBVF4HZd2UjGkb6t6uWoXjf6o7j7dXPQIL7vprcIT/67bTD6+5ocsU0UKShS2qWxueGDWuGfKHfOxHWrlTQg== + dependencies: + ajv "^7.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5: version "6.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" @@ -1809,6 +1352,26 @@ ajv@^6.12.2: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^7.0.0, ajv@^7.0.2, ajv@^7.0.3: + version "7.1.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.0.tgz#f982ea7933dc7f1012eae9eec5a86687d805421b" + integrity sha512-svS9uILze/cXbH0z2myCK2Brqprx/+JJYK5pHicT/GQiBfzzhUVAIT6MwqJg8y4xV/zoGsUeuPuwtoiKSGE15g== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ally.js@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/ally.js/-/ally.js-1.4.1.tgz#9fb7e6ba58efac4ee9131cb29aa9ee3b540bcf1e" @@ -1817,11 +1380,6 @@ ally.js@1.4.1: css.escape "^1.5.0" platform "1.3.3" -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - anser@1.4.9: version "1.4.9" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" @@ -1834,6 +1392,11 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -1871,14 +1434,14 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== @@ -1886,6 +1449,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1: "@types/color-name" "^1.1.1" color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.1.0.tgz#a436bcc51d81f4fab5080b2ba94242e377c41573" + integrity sha512-osxifZo3ar56+e8tdYreU6p8FZGciBHo5O0JoDAxMUqZuyNUb+yHEwYtJZ+Z32R459jEgtwVf1u8D7qYwU0l6w== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1951,7 +1519,7 @@ app-root-path@>=2.0.1: resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== -aproba@^1.1.1: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== @@ -1968,6 +1536,19 @@ archive-type@^4.0.0: dependencies: file-type "^4.2.0" +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2005,7 +1586,7 @@ array-find@^1.0.0: resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg= -array-includes@^3.0.3, array-includes@^3.1.1: +array-includes@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== @@ -2014,6 +1595,17 @@ array-includes@^3.0.3, array-includes@^3.1.1: es-abstract "^1.17.0" is-string "^1.0.5" +array-includes@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8" + integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + get-intrinsic "^1.0.1" + is-string "^1.0.5" + array-union@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -2036,7 +1628,7 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.flat@^1.2.1: +array.prototype.flat@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== @@ -2044,6 +1636,16 @@ array.prototype.flat@^1.2.1: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +array.prototype.flatmap@^1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9" + integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + function-bind "^1.1.1" + arrgv@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/arrgv/-/arrgv-1.0.2.tgz#025ed55a6a433cad9b604f8112fc4292715a6ec0" @@ -2080,13 +1682,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assert@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= - dependencies: - util "0.10.3" - assert@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" @@ -2100,10 +1695,10 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +ast-types@0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.2.tgz#df39b677a911a83f3a049644fb74fdded23cea48" + integrity sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA== astral-regex@^2.0.0: version "2.0.0" @@ -2147,34 +1742,44 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -ava@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/ava/-/ava-3.9.0.tgz#ac91eac980555fcc6c1b91872ac6923ff4c0ffae" - integrity sha512-EnK5I/AX1U5nF4X1YG3QQYg2+jWnpvMW3z2y096DBvbwITkq9rB7Gu1j5clWcuizAJUlYbvcX6YfP+zkRFgC8Q== +atomically@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/atomically/-/atomically-1.7.0.tgz#c07a0458432ea6dbc9a3506fffa424b48bccaafe" + integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w== + +auto-bind@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-1.2.1.tgz#807f7910b0210db9eefe133f3492c28e89698b96" + integrity sha512-/W9yj1yKmBLwpexwAujeD9YHwYmRuWFGV8HWE7smQab797VeHa4/cnE2NFeDhA+E+5e/OGBI8763EhLjfZ/MXA== + +ava@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/ava/-/ava-3.15.0.tgz#a239658ab1de8a29a243cc902e6b42e4574de2f0" + integrity sha512-HGAnk1SHPk4Sx6plFAUkzV/XC1j9+iQhOzt4vBly18/yo0AV8Oytx7mtJd/CR8igCJ5p160N/Oo/cNJi2uSeWA== dependencies: "@concordance/react" "^2.0.0" - acorn "^7.3.1" - acorn-walk "^7.1.1" - ansi-styles "^4.2.1" + acorn "^8.0.4" + acorn-walk "^8.0.0" + ansi-styles "^5.0.0" arrgv "^1.0.2" arrify "^2.0.1" callsites "^3.1.0" chalk "^4.1.0" - chokidar "^3.4.0" + chokidar "^3.4.3" chunkd "^2.0.1" ci-info "^2.0.0" - ci-parallel-vars "^1.0.0" + ci-parallel-vars "^1.0.1" clean-yaml-object "^0.1.0" cli-cursor "^3.1.0" cli-truncate "^2.1.0" - code-excerpt "^2.1.1" + code-excerpt "^3.0.0" common-path-prefix "^3.0.0" - concordance "^5.0.0" + concordance "^5.0.1" convert-source-map "^1.7.0" currently-unhandled "^0.4.1" - debug "^4.1.1" - del "^5.1.0" - emittery "^0.7.0" + debug "^4.3.1" + del "^6.0.0" + emittery "^0.8.0" equal-length "^1.0.0" figures "^3.2.0" globby "^11.0.1" @@ -2182,31 +1787,32 @@ ava@^3.9.0: import-local "^3.0.2" indent-string "^4.0.0" is-error "^2.2.2" - is-plain-object "^3.0.0" + is-plain-object "^5.0.0" is-promise "^4.0.0" - lodash "^4.17.15" + lodash "^4.17.20" matcher "^3.0.0" md5-hex "^3.0.1" - mem "^6.1.0" - ms "^2.1.2" - ora "^4.0.4" + mem "^8.0.0" + ms "^2.1.3" + ora "^5.2.0" + p-event "^4.2.0" p-map "^4.0.0" picomatch "^2.2.2" pkg-conf "^3.1.0" plur "^4.0.0" - pretty-ms "^7.0.0" + pretty-ms "^7.0.1" read-pkg "^5.2.0" resolve-cwd "^3.0.0" slash "^3.0.0" source-map-support "^0.5.19" - stack-utils "^2.0.2" + stack-utils "^2.0.3" strip-ansi "^6.0.0" - supertap "^1.0.0" + supertap "^2.0.0" temp-dir "^2.0.0" trim-off-newlines "^1.0.1" - update-notifier "^4.1.0" + update-notifier "^5.0.1" write-file-atomic "^3.0.3" - yargs "^15.3.1" + yargs "^16.2.0" aws-sign2@~0.7.0: version "0.7.0" @@ -2218,15 +1824,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== -babel-code-frame@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - babel-eslint@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -2239,13 +1836,6 @@ babel-eslint@^10.1.0: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-syntax-jsx@6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -2282,6 +1872,11 @@ base64-js@^1.0.2, base64-js@^1.2.3: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -2380,6 +1975,15 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" +bl@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" + integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bluebird-lst@^1.0.9: version "1.0.9" resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz#a64a0e4365658b9ab5fe875eb9dfb694189bb41c" @@ -2407,11 +2011,6 @@ bn.js@^5.1.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0" integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA== -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - boolean@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f" @@ -2431,6 +2030,20 @@ boxen@^4.2.0: type-fest "^0.8.1" widest-line "^3.1.0" +boxen@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.0.0.tgz#64fe9b16066af815f51057adcc800c3730120854" + integrity sha512-5bvsqw+hhgUi3oYGK0Vf4WpIkyemp60WBInn7+WNfoISzAqk/HX4L7WNROq38E6UR/y3YADpv6pEm4BfkeEAdA== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.0" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2528,15 +2141,26 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@4.12.0, browserslist@^4.0.0, browserslist@^4.11.1, browserslist@^4.8.5: - version "4.12.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" - integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== +browserslist@4.14.6: + version "4.14.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.6.tgz#97702a9c212e0c6b6afefad913d3a1538e348457" + integrity sha512-zeFYcUo85ENhc/zxHbiIp0LGzzTrE2Pv2JhxvS7kpUb9Q9D38kUX6Bie7pGutJ/5iF5rOxE7CepAuWD56xJ33A== dependencies: - caniuse-lite "^1.0.30001043" - electron-to-chromium "^1.3.413" - node-releases "^1.1.53" - pkg-up "^2.0.0" + caniuse-lite "^1.0.30001154" + electron-to-chromium "^1.3.585" + escalade "^3.1.1" + node-releases "^1.1.65" + +browserslist@^4.14.5: + version "4.16.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" + integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== + dependencies: + caniuse-lite "^1.0.30001181" + colorette "^1.2.1" + electron-to-chromium "^1.3.649" + escalade "^3.1.1" + node-releases "^1.1.70" buf-compare@^1.0.0: version "1.0.1" @@ -2576,6 +2200,14 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= +buffer@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" @@ -2593,6 +2225,14 @@ buffer@^5.2.1: base64-js "^1.0.2" ieee754 "^1.1.4" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builder-util-runtime@8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.0.tgz#e48ad004835c8284662e8eaf47a53468c66e8e8d" @@ -2626,29 +2266,10 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= -cacache@13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-13.0.1.tgz#a8000c21697089082f85287a1aec6e382024a71c" - integrity sha512-5ZvAxd05HDDU+y9BVvcqYu2LLXmPnQ0hW62h32g4xBTgL/MppR4/04NHfj/ycM2y6lmTnbw6HVi+1eN0Psba6w== - dependencies: - chownr "^1.1.2" - figgy-pudding "^3.5.1" - fs-minipass "^2.0.0" - glob "^7.1.4" - graceful-fs "^4.2.2" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.0.0" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - p-map "^3.0.0" - promise-inflight "^1.0.1" - rimraf "^2.7.1" - ssri "^7.0.0" - unique-filename "^1.1.1" +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== cacache@^12.0.2: version "12.0.4" @@ -2712,30 +2333,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2749,19 +2359,14 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" - integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== dependencies: - camelcase "^4.1.0" - map-obj "^2.0.0" - quick-lru "^1.0.0" - -camelcase@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" - integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" @@ -2773,25 +2378,25 @@ camelcase@^2.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= +camelcase@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78" + integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ== -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" +camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001043: - version "1.0.30001066" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz#0a8a58a10108f2b9bf38e7b65c237b12fd9c5f04" - integrity sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw== +caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001113, caniuse-lite@^1.0.30001154: + version "1.0.30001185" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz" + integrity sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg== + +caniuse-lite@^1.0.30001181: + version "1.0.30001197" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001197.tgz#47ad15b977d2f32b3ec2fe2b087e0c50443771db" + integrity sha512-8aE+sqBqtXz4G8g35Eg/XEaFr2N7rd/VQ6eABGBmNtcB8cN6qNJhMi6oSFy4UWWZgqgL3filHT8Nha4meu3tsw== caseless@~0.12.0: version "0.12.0" @@ -2808,6 +2413,15 @@ caw@^2.0.0, caw@^2.0.1: tunnel-agent "^0.6.0" url-to-options "^1.0.1" +chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, 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" + supports-color "^5.3.0" + chalk@4.0.0, chalk@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" @@ -2816,7 +2430,7 @@ chalk@4.0.0, chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -2827,15 +2441,6 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, 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" - supports-color "^5.3.0" - chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -2857,7 +2462,22 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@2.1.8, chokidar@^2.1.8: +chokidar@3.4.3, chokidar@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" + integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.1.2" + +chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -2876,10 +2496,10 @@ chokidar@2.1.8, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.3.0, chokidar@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" - integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== +chokidar@^3.4.3: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -2887,11 +2507,11 @@ chokidar@^3.3.0, chokidar@^3.4.0: is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.4.0" + readdirp "~3.5.0" optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.1" -chownr@^1.1.1, chownr@^1.1.2: +chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== @@ -2918,7 +2538,7 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== -ci-parallel-vars@^1.0.0: +ci-parallel-vars@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz#e87ff0625ccf9d286985b29b4ada8485ca9ffbc2" integrity sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg== @@ -2975,6 +2595,11 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2989,10 +2614,10 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5" - integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w== +cli-spinners@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" + integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== cli-truncate@^2.1.0: version "2.1.0" @@ -3016,14 +2641,14 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" clone-response@1.0.2, clone-response@^1.0.2: version "1.0.2" @@ -3037,22 +2662,18 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -code-excerpt@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-2.1.1.tgz#5fe3057bfbb71a5f300f659ef2cc0a47651ba77c" - integrity sha512-tJLhH3EpFm/1x7heIW0hemXJTUU5EWl2V0EIX558jp05Mt1U6DVryCgkp3l37cxqs+DNbNgxG43SkwJXpQ14Jw== +code-excerpt@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/code-excerpt/-/code-excerpt-3.0.0.tgz#fcfb6748c03dba8431c19f5474747fad3f250f10" + integrity sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw== dependencies: convert-to-spaces "^1.0.1" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -3085,21 +2706,26 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== +color-string@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" + integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" -color@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" - integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg== +color@^3.1.1, color@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" + integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== dependencies: color-convert "^1.9.1" - color-string "^1.5.2" + color-string "^1.5.4" + +colorette@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" + integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" @@ -3162,10 +2788,10 @@ concat-stream@^1.5.0, concat-stream@^1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" -concordance@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.0.tgz#6d4552f76c78301dd65e748c26af2cf131f9dd49" - integrity sha512-stOCz9ffg0+rytwTaL2njUOIyMfANwfwmqc9Dr4vTUS/x/KkVFlWx9Zlzu6tHYtjKxxaCF/cEAZgPDac+n35sg== +concordance@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/concordance/-/concordance-5.0.2.tgz#c242e59b7ee507491e16e9bea85967f7afccec0a" + integrity sha512-hC63FKdGM9tBcd4VQIa+LQjmrgorrnxESb8B3J21Qe/FzL0blBv0pb8iNyymt+bmsvGSUqO0uhPi2ZSLgLtLdg== dependencies: date-time "^3.1.0" esutils "^2.0.3" @@ -3187,21 +2813,22 @@ conf@^1.4.0: pkg-up "^2.0.0" write-file-atomic "^2.3.0" -conf@^6.2.1: - version "6.2.4" - resolved "https://registry.yarnpkg.com/conf/-/conf-6.2.4.tgz#49d23c4e21ef2ac2860f7b5ed25b7b7e484f769f" - integrity sha512-GjgyPRLo1qK1LR9RWAdUagqo+DP18f5HWCFk4va7GS+wpxQTOzfuKTwKOvGW2c01/YXNicAyyoyuSddmdkBzZQ== - dependencies: - ajv "^6.10.2" - debounce-fn "^3.0.1" - dot-prop "^5.0.0" +conf@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/conf/-/conf-9.0.2.tgz#943589602b1ce274d9234265314336a698972bc5" + integrity sha512-rLSiilO85qHgaTBIIHQpsv8z+NnVfZq3cKuYNCXN1AOqPzced0GWZEe/A517VldRLyQYXUMyV+vszavE2jSAqw== + dependencies: + ajv "^7.0.3" + ajv-formats "^1.5.1" + atomically "^1.7.0" + debounce-fn "^4.0.0" + dot-prop "^6.0.1" env-paths "^2.2.0" - json-schema-typed "^7.0.1" - make-dir "^3.0.0" - onetime "^5.1.0" - pkg-up "^3.0.1" - semver "^6.2.0" - write-file-atomic "^3.0.0" + json-schema-typed "^7.0.3" + make-dir "^3.1.0" + onetime "^5.1.2" + pkg-up "^3.1.0" + semver "^7.3.4" config-chain@^1.1.11: version "1.1.12" @@ -3223,16 +2850,21 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" -confusing-browser-globals@1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" - integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== +confusing-browser-globals@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" + integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== console-browserify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + console-stream@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/console-stream/-/console-stream-0.1.1.tgz#a095fe07b20465955f2fafd28b5d72bccd949d44" @@ -3302,19 +2934,6 @@ core-assert@^0.2.0: buf-compare "^1.0.0" is-error "^2.2.0" -core-js-compat@^3.6.2: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.5.tgz#2a51d9a4e25dfd6e690251aa81f99e3c05481f1c" - integrity sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng== - dependencies: - browserslist "^4.8.5" - semver "7.0.0" - -core-js-pure@^3.0.0: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" - integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== - core-js@^3.6.4: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" @@ -3325,16 +2944,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -3346,6 +2955,27 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cp-file@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-9.0.0.tgz#11986b27fd0ea7e6eadaf8a86cf16ba7e9b830ce" + integrity sha512-hNEM3AuZz84zOPgL8ozJ057+DjuD6LpwyYNiyTsLDU71Ke2E3KV909xhGYLK7WQ0srJRwzs/1+bSE1pk/HRC6w== + dependencies: + graceful-fs "^4.1.2" + make-dir "^3.0.0" + nested-error-stacks "^2.0.0" + p-event "^4.1.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3382,13 +3012,17 @@ create-react-context@^0.1.5: resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.1.6.tgz#0f425931d907741127acc6e31acb4f9015dd9fdc" integrity sha512-eCnYYEUEc5i32LHwpE/W7NlddOB9oHwsPaWtWzYtflNkkwa3IfindIcoXdVWs12zCbwaMCavKNu84EXogVIWHw== -cross-fetch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.4.tgz#7bef7020207e684a7638ef5f2f698e24d9eb283c" - integrity sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw== +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== dependencies: - node-fetch "2.6.0" - whatwg-fetch "3.0.0" + node-fetch "2.6.1" cross-spawn@^5.0.1: version "5.1.0" @@ -3399,7 +3033,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -3410,7 +3044,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -3419,7 +3053,7 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" -crypto-browserify@^3.11.0: +crypto-browserify@3.12.0, crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== @@ -3446,73 +3080,23 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-loader@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.3.tgz#95ac16468e1adcd95c844729e0bb167639eb0bcf" - integrity sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw== +css-loader@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" + integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== dependencies: - camelcase "^5.3.1" + camelcase "^6.0.0" cssesc "^3.0.0" icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.27" + loader-utils "^2.0.0" + postcss "^7.0.32" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" + postcss-modules-local-by-default "^3.0.3" postcss-modules-scope "^2.2.0" postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.3" - schema-utils "^2.6.6" - semver "^6.3.0" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@1.0.0-alpha.39: - version "1.0.0-alpha.39" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" - integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== - dependencies: - mdn-data "2.0.6" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1" - integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw== + postcss-value-parser "^4.1.0" + schema-utils "^2.7.1" + semver "^7.3.2" css.escape@^1.5.0: version "1.5.1" @@ -3534,80 +3118,26 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== +cssnano-preset-simple@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-1.2.1.tgz#8976013114b1fc4718253d30f21aaed1780fb80e" + integrity sha512-B2KahOIFTV6dw5Ioy9jHshTh/vAYNnUB2enyWRgnAEg3oJBjI/035ExpePaMqS2SwpbH7gCgvQqwpMBH6hTJSw== dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + caniuse-lite "^1.0.30001093" + postcss "^7.0.32" -cssnano@4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== +cssnano-simple@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-1.2.1.tgz#6de5d9dd75774bc8f31767573410a952c7dd8a12" + integrity sha512-9vOyjw8Dj/T12kIOnXPZ5VnEIo6F3YMaIn0wqJXmn277R58cWpI3AvtdlCBtohX7VAUNYcyk2d0dKcXXkb5I6Q== dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" + cssnano-preset-simple "1.2.1" + postcss "^7.0.32" -csso@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.3.tgz#0d9985dc852c7cc2b2cacfbbe1079014d1a8e903" - integrity sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ== - dependencies: - css-tree "1.0.0-alpha.39" +csstype@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.2.tgz#ee5ff8f208c8cd613b389f7b222c9801ca62b3f7" + integrity sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw== currently-unhandled@^0.4.1: version "0.4.1" @@ -3650,12 +3180,12 @@ date-time@^3.1.0: dependencies: time-zone "^1.0.0" -debounce-fn@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-3.0.1.tgz#034afe8b904d985d1ec1aa589cd15f388741d680" - integrity sha512-aBoJh5AhpqlRoHZjHmOzZlRx+wz2xVwGL9rjs+Kj0EWUrL4/h4K7OD176thl2Tdoqui/AaA4xhHrNArGLAaI3Q== +debounce-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7" + integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ== dependencies: - mimic-fn "^2.1.0" + mimic-fn "^3.0.0" debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" @@ -3671,7 +3201,14 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -decamelize-keys@^1.0.0: +debug@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -3696,6 +3233,20 @@ decompress-response@^3.2.0, decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" @@ -3754,7 +3305,7 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -3807,18 +3358,18 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" -del@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/del/-/del-5.1.0.tgz#d9487c94e367410e6eff2925ee58c0c84a75b3a7" - integrity sha512-wH9xOVHnczo9jN2IW68BabcecVPxacIA3g/7z6vhSU/4stOKQzeCRK0yD0A24WiAAUJmmVpWqrERcTxnLo3AnA== +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== dependencies: - globby "^10.0.1" - graceful-fs "^4.2.2" + globby "^11.0.1" + graceful-fs "^4.2.4" is-glob "^4.0.1" is-path-cwd "^2.2.0" - is-path-inside "^3.0.1" - p-map "^3.0.0" - rimraf "^3.0.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" slash "^3.0.0" delay@^4.3.0: @@ -3831,6 +3382,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -3839,12 +3400,17 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + detect-node@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== -diff@^4.0.2: +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -3920,12 +3486,22 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@0, dom-serializer@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== +dom-serializer@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.1.0.tgz#5f7c828f1bfc44887dc2a315ab5c45691d544b58" + integrity sha512-ox7bvGXt2n+uLWtCRLybYx60IrOlWL/aCebWJk1T0d4m3y2tzf4U3ij9wBMUb6YJZpz06HCCYuyCDveE2xXmzQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + entities "^2.0.0" + +dom-serializer@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== dependencies: domelementtype "^2.0.1" + domhandler "^4.0.0" entities "^2.0.0" domain-browser@^1.1.1: @@ -3933,48 +3509,54 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - domelementtype@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== -domhandler@3.0.0, domhandler@^3.0.0: +domelementtype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + +domhandler@3.3.0, domhandler@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + +domhandler@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw== dependencies: domelementtype "^2.0.1" -domutils@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" - integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg== +domhandler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" + integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== dependencies: - dom-serializer "^0.2.1" - domelementtype "^2.0.1" - domhandler "^3.0.0" + domelementtype "^2.1.0" -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== +domutils@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.2.tgz#7ee5be261944e1ad487d9aa0616720010123922b" + integrity sha512-NKbgaM8ZJOecTZsIzW5gSuplsX2IWW2mIK7xVr8hTQF2v1CJWTmLZ1HOCh5sH+IzVPAGE5IucooOkvwBRAdowA== dependencies: - dom-serializer "0" - domelementtype "1" + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^3.3.0" -domutils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.1.0.tgz#7ade3201af43703fde154952e3a868eb4b635f16" - integrity sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg== +domutils@^2.4.2: + version "2.4.4" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" + integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== dependencies: - dom-serializer "^0.2.1" + dom-serializer "^1.0.1" domelementtype "^2.0.1" - domhandler "^3.0.0" + domhandler "^4.0.0" dot-prop@^4.1.0: version "4.2.0" @@ -3983,13 +3565,20 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" -dot-prop@^5.0.0, dot-prop@^5.2.0: +dot-prop@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== dependencies: is-obj "^2.0.0" +dot-prop@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" + integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA== + dependencies: + is-obj "^2.0.0" + dotenv-expand@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" @@ -4065,10 +3654,10 @@ ejs@^3.1.2: dependencies: jake "^10.6.1" -electron-better-ipc@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/electron-better-ipc/-/electron-better-ipc-1.0.1.tgz#ec5f397d44ce1225d826461ff93881912fe68490" - integrity sha512-4okAJoeY2+NXsPLYE+ccVn2NSDRCqMAAV1Kp5YzNRGki505p9nXuM6+d2QAlZ1OWyQxXmDm0B+eezjQL55uF/A== +electron-better-ipc@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/electron-better-ipc/-/electron-better-ipc-1.1.1.tgz#bbb1aa567b97a0cdcf61bcc464a35df4ec6fd664" + integrity sha512-Y8Ty54HJtzT6aWNTyE092W/7q8y3+yJz0XvRpOKGIsLDL2H7oWIFkUegmB+6h5SszjtBiQFpmNmHOKfkDZ8HbQ== dependencies: serialize-error "^5.0.0" @@ -4160,18 +3749,34 @@ electron-publish@22.6.1: lazy-val "^1.0.4" mime "^2.4.5" -electron-store@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-5.1.1.tgz#3040e5b4ad25d2e4caea59d505646c1e7c94a09b" - integrity sha512-FLidOVE8JVCdJXHd7xY/JojKJ2r2WNmWt0O/LlX2LuSVV7dkG2RSy2/Gm2LFw8OKDfrNBd9c/s4X1ikMrJEUKg== +electron-store@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-7.0.2.tgz#e45a54d092ea43e2a953619ed4473893d448dfdf" + integrity sha512-tSUeHF9qdiPirph8JKJvIIcdVb3wYwgHUJCE38SJq4L08Op2z1+u8DSQ42Nvx34TKvb2lkQkByV0tHh6xBxdEQ== dependencies: - conf "^6.2.1" - type-fest "^0.7.1" + conf "^9.0.0" + type-fest "^0.20.2" + +electron-timber@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/electron-timber/-/electron-timber-0.5.1.tgz#a9b78ec097611ad5d438dc4635f842dc484d99fc" + integrity sha512-5CTijMwDeIdwm7GzYqUIRI2SbTjTJq2khexNmqzuZTFi7QmsbeJyvMSHvmTLztQGGmKeAGp4ym2FYRBa+OA5bA== + dependencies: + auto-bind "^1.2.0" + chalk "^2.4.1" + electron-util "^0.8.2" + randoma "^1.2.0" + split2 "^2.2.0" + +electron-to-chromium@^1.3.585: + version "1.3.633" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe" + integrity sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA== -electron-to-chromium@^1.3.413: - version "1.3.451" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.451.tgz#0c075af3e2f06d706670bde0279432802ca8c83f" - integrity sha512-2fvco0F2bBIgqzO8GRP0Jt/91pdrf9KfZ5FsmkYkjERmIJG585cFeFZV4+CO6oTmU3HmCTgfcZuEa7kW8VUh3A== +electron-to-chromium@^1.3.649: + version "1.3.684" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.684.tgz#053fbb0a4b2d5c076dfa6e1d8ecd06a3075a558a" + integrity sha512-GV/vz2EmmtRSvfGSQ5A0Lucic//IRSDijgL15IgzbBEEnp4rfbxeUSZSlBfmsj7BQvE4sBdgfsvPzLCnp6L21w== electron-updater@^4.3.1: version "4.3.1" @@ -4225,10 +3830,17 @@ electron-util@^0.4.1: dependencies: electron-is-dev "^0.3.0" -electron@8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/electron/-/electron-8.2.4.tgz#c4e51ca8e84b5a5beaaabdae1024bd52ba487ba4" - integrity sha512-Lle0InIgSAHZxD5KDY0wZ1A2Zlc6GHwMhAxoHMzn05mndyP1YBkCYHc0TDDofzUTrsLFofduPjlknO5Oj9fTPA== +electron-util@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/electron-util/-/electron-util-0.8.2.tgz#9ec18f54141192e3fcbfe1d6dfd22780de87ffed" + integrity sha512-35gFE5oxi39ZBchWWvMNiKyUaNp3OmH3S1UruM+P0ZLpnOgJUMdBxl514f6f9QsShn7Lj3u9t31tavN/PzmhPw== + dependencies: + electron-is-dev "^0.3.0" + +electron@^9.4.4: + version "9.4.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-9.4.4.tgz#2a74a0655a74bd326216672c5ae6ed3a44451446" + integrity sha512-dcPlTrMWQu5xuSm6sYV42KK/BRIqh3erM8v/WtZqaDmG7pkCeJpvw26Dgbqhdt78XmqqGiN96giEe6A3S9vpAQ== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" @@ -4247,10 +3859,10 @@ elliptic@^6.0.0, elliptic@^6.5.2: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" -emittery@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.0.tgz#0f0789ea90e03f3de7865feb806e4f0916d16c93" - integrity sha512-/kshvS+tZaggOPQDLGzXopumRRIzxciGILDlYTGIU+PmqbSfhn4wDVphFPry4H+2TNl2QxLduexPhxcWLULA5A== +emittery@^0.8.0: + version "0.8.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" + integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== emoji-regex@^7.0.1: version "7.0.3" @@ -4284,7 +3896,7 @@ encoding@^0.1.12: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -4307,15 +3919,22 @@ enhanced-resolve@^0.9.1: memory-fs "^0.2.0" tapable "^0.1.8" -enhanced-resolve@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== +enhanced-resolve@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" + integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" tapable "^1.0.0" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + ensure-error@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ensure-error/-/ensure-error-2.1.0.tgz#f11fbe383c0cf4a54850ac77acceb7bc06e0f99d" @@ -4326,10 +3945,10 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.2.tgz#ac74db0bba8d33808bbf36809c3a5c3683531436" integrity sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw== -env-editor@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.1.tgz#77011e08ce45f46e404e8d996b465c684ca57502" - integrity sha512-suh+Vm00GnPQgXpmONTkcUT9LgBSL6sJrRnJxbykT0j+ONjzmIS+1U3ne467ArdZN/42/npp+GnhtwkLQ+vUjw== +env-editor@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861" + integrity sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA== env-paths@^1.0.0: version "1.0.0" @@ -4377,6 +3996,26 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstrac string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-abstract@^1.18.0-next.1: + version "1.18.0-next.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" + integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-negative-zero "^2.0.1" + is-regex "^1.1.1" + object-inspect "^1.9.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.3" + string.prototype.trimstart "^1.0.3" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4417,6 +4056,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-goat@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" @@ -4445,55 +4089,53 @@ eslint-ast-utils@^1.1.0: lodash.get "^4.4.2" lodash.zip "^4.2.0" -eslint-config-prettier@^6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" - integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== - dependencies: - get-stdin "^6.0.0" +eslint-config-prettier@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz#f4a4bd2832e810e8cc7c1411ec85b3e85c0c53f9" + integrity sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg== eslint-config-xo-react@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/eslint-config-xo-react/-/eslint-config-xo-react-0.23.0.tgz#a6e4cfc3e1231cf9291fa19000f559dc53183090" integrity sha512-yBNoMF6COjJLRijlWksC1Jws6CGWHMgTfR/PAReCdZUo5cg/ZsyNeZx/fntyU4SbARwW2RvuezMeKle+jI5yoA== -eslint-config-xo-typescript@^0.28.0: - version "0.28.0" - resolved "https://registry.yarnpkg.com/eslint-config-xo-typescript/-/eslint-config-xo-typescript-0.28.0.tgz#312286a90881097865f73e3237081a12e90f16c4" - integrity sha512-q+mBhTikLjrPOszx6gezd6ZxrbrSHeBp/Z8gGgPowBYvBYG78llfQZxOcsbKN6aM8+S/OFAJP9bk5w/roLDFTA== +eslint-config-xo-typescript@^0.37.0: + version "0.37.0" + resolved "https://registry.yarnpkg.com/eslint-config-xo-typescript/-/eslint-config-xo-typescript-0.37.0.tgz#fead961a5cf1874b57372c63e5417cb5fd58afb3" + integrity sha512-eFRj4xMhiMExCb+H/wyXC1OwTKJiOQ+Qd9myoZLWdyR0X8PAjtzPRfxSOyh2fkBnZvFCDY0DuqcLkPSDSe4Ybg== -eslint-config-xo@^0.29.0: - version "0.29.1" - resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.29.1.tgz#876e29b2f4711f2fd365885b09b9536b6ef328dc" - integrity sha512-RDjeKh8CV0/EH4utW/6uOkwJJOOU+rX3uE5eUBOamcLNe4lNjyo8kSt3B6DzAm1L/1tWGikI7LFNVY9gG7PDQw== +eslint-config-xo@^0.34.0: + version "0.34.0" + resolved "https://registry.yarnpkg.com/eslint-config-xo/-/eslint-config-xo-0.34.0.tgz#c158ab361aec62727219ed3b1971c24311cd9ad4" + integrity sha512-lbpHNfVmSrEQsHsyQ44wY8VA5mEI4Jri5Dk1BJUH0jiCw6nTkj16VQeHiuWzIBJ/uf+mF2FzSqLVf3tD1cHhYA== dependencies: - confusing-browser-globals "1.0.9" + confusing-browser-globals "1.0.10" -eslint-formatter-pretty@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-3.0.1.tgz#97603fcb2ddcc6dd60662d6e9f327a734cc55a54" - integrity sha512-hhQ/ASD4i6BAEalcEfUxesFtJFftT8xFsimCzUpPbTzygJ4J17yCGcJ3XKCB2g7XTJTv0pi7rVTadfHVmtfSRA== +eslint-formatter-pretty@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-4.0.0.tgz#dc15f3bf4fb51b7ba5fbedb77f57ba8841140ce2" + integrity sha512-QgdeZxQwWcN0TcXXNZJiS6BizhAANFhCzkE7Yl9HKB7WjElzwED6+FbbZB2gji8ofgJTGPqKm6VRCNT3OGCeEw== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" eslint-rule-docs "^1.1.5" - log-symbols "^3.0.0" - plur "^3.0.1" + log-symbols "^4.0.0" + plur "^4.0.0" string-width "^4.2.0" supports-hyperlinks "^2.0.0" -eslint-import-resolver-node@^0.3.2: - version "0.3.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" - integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== +eslint-import-resolver-node@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== dependencies: debug "^2.6.9" resolve "^1.13.1" -eslint-import-resolver-webpack@^0.12.1: - version "0.12.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.12.1.tgz#771ae561e887ca4e53ee87605fbb36c5e290b0f5" - integrity sha512-O/sUAXk6GWrICiN8JUkkjdt9uZpqZHP+FVnTxtEILL6EZMaPSrnP4lGPSFwcKsv7O211maqq4Nz60+dh236hVg== +eslint-import-resolver-webpack@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz#5cb19cf4b6996c8a2514aeb10f909e2c70488dc3" + integrity sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw== dependencies: array-find "^1.0.0" debug "^2.6.9" @@ -4506,7 +4148,7 @@ eslint-import-resolver-webpack@^0.12.1: resolve "^1.13.1" semver "^5.7.1" -eslint-module-utils@^2.4.1: +eslint-module-utils@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== @@ -4514,14 +4156,15 @@ eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-ava@^10.0.1: - version "10.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-ava/-/eslint-plugin-ava-10.3.0.tgz#8c388520ae95bc4681d06f25fbcd0d70f67163e1" - integrity sha512-rNijYwBo+28ahXvifTx3IYzsNjtArAeyowBJ1Rd5dCtW7f0y5aSTBU7sXBpU5o5Eh5/FZvz3lSLVLac8ExYTtA== +eslint-plugin-ava@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-ava/-/eslint-plugin-ava-11.0.0.tgz#92d98694b4af6d6f2353613af06301d44d6b13a6" + integrity sha512-UMGedfl/gIKx1tzjGtAsTSJgowyAEZU2VWmpoWXYcuuV4B2H4Cu90yuMgMPEVt1mQlIZ21L7YM2CSpHUFJo/LQ== dependencies: deep-strict-equal "^0.2.0" enhance-visitors "^1.0.0" - espree "^6.1.2" + eslint-utils "^2.1.0" + espree "^7.2.0" espurify "^2.0.1" import-modules "^2.0.0" micro-spelling-correcter "^1.1.1" @@ -4536,7 +4179,7 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-eslint-comments@^3.1.2: +eslint-plugin-eslint-comments@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz#9e1cd7b4413526abb313933071d7aba05ca12ffa" integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== @@ -4544,23 +4187,24 @@ eslint-plugin-eslint-comments@^3.1.2: escape-string-regexp "^1.0.5" ignore "^5.0.5" -eslint-plugin-import@^2.20.1: - version "2.20.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" - integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== +eslint-plugin-import@^2.22.1: + version "2.22.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" + integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== dependencies: - array-includes "^3.0.3" - array.prototype.flat "^1.2.1" + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" contains-path "^0.1.0" debug "^2.6.9" doctrine "1.5.0" - eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.1" + eslint-import-resolver-node "^0.3.4" + eslint-module-utils "^2.6.0" has "^1.0.3" minimatch "^3.0.4" - object.values "^1.1.0" + object.values "^1.1.1" read-pkg-up "^2.0.0" - resolve "^1.12.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" eslint-plugin-no-use-extend-native@^0.5.0: version "0.5.0" @@ -4572,7 +4216,7 @@ eslint-plugin-no-use-extend-native@^0.5.0: is-obj-prop "^1.0.0" is-proto-prop "^2.0.0" -eslint-plugin-node@^11.0.0: +eslint-plugin-node@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== @@ -4584,10 +4228,10 @@ eslint-plugin-node@^11.0.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-prettier@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" - integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== +eslint-plugin-prettier@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -4596,51 +4240,60 @@ eslint-plugin-promise@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-plugin-react-hooks@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35" - integrity sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw== +eslint-plugin-react-hooks@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556" + integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ== -eslint-plugin-react@^7.19.0: - version "7.20.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz#f98712f0a5e57dfd3e5542ef0604b8739cd47be3" - integrity sha512-rqe1abd0vxMjmbPngo4NaYxTcR3Y4Hrmc/jg4T+sYz63yqlmJRknpEQfmWY+eDWPuMmix6iUIK+mv0zExjeLgA== +eslint-plugin-react@^7.22.0: + version "7.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.22.0.tgz#3d1c542d1d3169c45421c1215d9470e341707269" + integrity sha512-p30tuX3VS+NWv9nQot9xIGAHBXR0+xJVaZriEsHoJrASGCJZDJ8JLNM0YqKqI0AKm6Uxaa1VUHoNEibxRCMQHA== dependencies: array-includes "^3.1.1" + array.prototype.flatmap "^1.2.3" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.2.3" - object.entries "^1.1.1" + jsx-ast-utils "^2.4.1 || ^3.0.0" + object.entries "^1.1.2" object.fromentries "^2.0.2" object.values "^1.1.1" prop-types "^15.7.2" - resolve "^1.15.1" + resolve "^1.18.1" string.prototype.matchall "^4.0.2" - xregexp "^4.3.0" -eslint-plugin-unicorn@^19.0.0: - version "19.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-19.0.1.tgz#05eef02f33426b8aa4f21cd5e4785b456335b85b" - integrity sha512-fu0/h5mHXfBC6EkA3i2vCjsfC8j53+T9txGhNL4fpxJ+1JKsUKfv+tmXDgy0XnLHhFjnOZp4tRWJWbcykeIP2Q== +eslint-plugin-unicorn@^25.0.1: + version "25.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-25.0.1.tgz#bedcbc9b817042a44f6501584e8266741120f023" + integrity sha512-MEyEWoyou/qhJH6rEER9YHACtCsQT+eewc6Fdxbi2eiTvsGrBR8JZMA6qaeof3oMQeRxOpaERoBKzU7R5c4A/w== dependencies: ci-info "^2.0.0" clean-regexp "^1.0.0" eslint-ast-utils "^1.1.0" - eslint-template-visitor "^1.1.0" - eslint-utils "^2.0.0" + eslint-template-visitor "^2.2.1" + eslint-utils "^2.1.0" import-modules "^2.0.0" - lodash "^4.17.15" + lodash "^4.17.20" + pluralize "^8.0.0" read-pkg-up "^7.0.1" regexp-tree "^0.1.21" reserved-words "^0.1.2" safe-regex "^2.1.1" - semver "^7.1.3" + semver "^7.3.4" eslint-rule-docs@^1.1.5: version "1.1.184" resolved "https://registry.yarnpkg.com/eslint-rule-docs/-/eslint-rule-docs-1.1.184.tgz#338942d9bb97aa135cd2d1115880855ea504ba35" integrity sha512-/BdfhXEVx+GwwaJfyyEC/j5fZ5WkGIMXpX3WwI/KNhVgrRYRk5dSMCmMzCrOcIr2P5jqgGnlfeY9A8eFnElEBA== +eslint-scope@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" + integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -4657,21 +4310,23 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-template-visitor@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-1.1.0.tgz#f090d124d1a52e05552149fc50468ed59608b166" - integrity sha512-Lmy6QVlmFiIGl5fPi+8ACnov3sare+0Ouf7deJAGGhmUfeWJ5fVarELUxZRpsZ9sHejiJUq8626d0dn9uvcZTw== +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - eslint-visitor-keys "^1.1.0" - espree "^6.1.1" - multimap "^1.0.2" + esrecurse "^4.3.0" + estraverse "^4.1.1" -eslint-utils@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== +eslint-template-visitor@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-2.2.1.tgz#2dccb1ab28fa7429e56ba6dd0144def2d89bc2d6" + integrity sha512-q3SxoBXz0XjPGkUpwGVAwIwIPIxzCAJX1uwfVc8tW3v7u/zS7WXNH3I2Mu2MDz2NgSITAyKLRaQFPHu/iyKxDQ== dependencies: - eslint-visitor-keys "^1.1.0" + babel-eslint "^10.1.0" + eslint-visitor-keys "^1.3.0" + esquery "^1.3.1" + multimap "^1.1.0" eslint-utils@^2.0.0: version "2.0.0" @@ -4680,62 +4335,88 @@ eslint-utils@^2.0.0: dependencies: eslint-visitor-keys "^1.1.0" +eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== -eslint@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" - integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== +eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^7.17.0: + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" + integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== dependencies: - "@babel/code-frame" "^7.0.0" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.3.0" ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" + chalk "^4.0.0" + cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.3" - eslint-visitor-keys "^1.1.0" - espree "^6.1.2" - esquery "^1.0.1" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" + levn "^0.4.1" + lodash "^4.17.20" minimatch "^3.0.4" - mkdirp "^0.5.1" natural-compare "^1.4.0" - optionator "^0.8.3" + optionator "^0.9.1" progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.1, espree@^6.1.2: - version "6.2.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" - integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== +espree@^7.2.0, espree@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" + integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== dependencies: - acorn "^7.1.1" + acorn "^7.4.0" acorn-jsx "^5.2.0" - eslint-visitor-keys "^1.1.0" + eslint-visitor-keys "^1.3.0" + +espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.3.1" + eslint-visitor-keys "^1.3.0" esprima@^4.0.0: version "4.0.1" @@ -4747,12 +4428,19 @@ espurify@^2.0.1: resolved "https://registry.yarnpkg.com/espurify/-/espurify-2.0.1.tgz#c25b3bb613863daa142edcca052370a1a459f41d" integrity sha512-7w/dUrReI/QbJFHRwfomTlkQOXaB1NuCrBRn5Y26HXn5gvh18/19AgLbayVrNxXQfkckvgrJloWyvZDuJ7dhEA== -esquery@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" - integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== +esquery@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: - estraverse "^5.0.0" + estraverse "^5.1.0" esrecurse@^4.1.0: version "4.2.1" @@ -4761,21 +4449,38 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + estraverse@^4.1.0, estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" - integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2, esutils@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +etag@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" @@ -4873,6 +4578,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + executable@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -4893,6 +4613,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + ext-list@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" @@ -5000,7 +4725,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.0.3, fast-glob@^3.1.1: +fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ== @@ -5017,7 +4742,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -5056,19 +4781,19 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -figures@^3.0.0, figures@^3.2.0: +figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-icon@^3.0.0: version "3.1.0" @@ -5216,6 +4941,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-versions@^3.0.0, find-versions@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -5223,19 +4956,18 @@ find-versions@^3.0.0, find-versions@^3.2.0: dependencies: semver-regex "^2.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== flush-write-stream@^1.0.0: version "1.1.1" @@ -5255,20 +4987,6 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -fork-ts-checker-webpack-plugin@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-3.1.1.tgz#a1642c0d3e65f50c2cc1742e9c0a80f441f86b19" - integrity sha512-DuVkPNrM12jR41KM2e+N+styka0EgLkTnXmNcXdgOM37vtGeY+oCBK/Jx0hzSeEU6memFCtWb4htrHPMDfwwUQ== - dependencies: - babel-code-frame "^6.22.0" - chalk "^2.4.1" - chokidar "^3.3.0" - micromatch "^3.1.10" - minimatch "^3.0.4" - semver "^5.6.0" - tapable "^1.0.0" - worker-rpc "^0.1.0" - form-data@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -5326,12 +5044,15 @@ fs-extra@^9.0.0: jsonfile "^6.0.1" universalify "^1.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== +fs-extra@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc" + integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ== dependencies: - minipass "^3.0.0" + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^1.0.0" fs-write-stream-atomic@^1.0.8: version "1.0.10" @@ -5361,6 +5082,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -5371,11 +5097,39 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -get-caller-file@^2.0.1: +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: 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-intrinsic@^1.0.1, get-intrinsic@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-proxy@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" @@ -5393,15 +5147,10 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== - -get-stdin@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" - integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== +get-stdin@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" @@ -5430,6 +5179,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -5452,6 +5206,11 @@ gifsicle@^5.0.0: execa "^1.0.0" logalot "^2.0.0" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -5477,7 +5236,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -5509,6 +5268,13 @@ global-dirs@^2.0.1: dependencies: ini "^1.3.5" +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + global-tunnel-ng@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" @@ -5538,20 +5304,6 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" -globby@^10.0.1: - version "10.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" - integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.0.3" - glob "^7.1.3" - ignore "^5.1.1" - merge2 "^1.2.3" - slash "^3.0.0" - globby@^11.0.1: version "11.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" @@ -5564,7 +5316,7 @@ globby@^11.0.1: merge2 "^1.3.0" slash "^3.0.0" -globby@^9.0.0: +globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== @@ -5643,10 +5395,10 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1. resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -graceful-fs@^4.2.2: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== "graceful-readlink@>= 1.0.0": version "1.0.1" @@ -5666,6 +5418,11 @@ har-validator@~5.1.3: ajv "^6.5.5" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -5700,6 +5457,11 @@ has-to-string-tag-x@^1.2.0: dependencies: has-symbol-support-x "^1.4.1" +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -5736,7 +5498,7 @@ has-yarn@^2.1.0: resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== -has@^1.0.0, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -5760,10 +5522,10 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hmac-drbg@^1.0.0: version "1.0.1" @@ -5786,29 +5548,21 @@ hosted-git-info@^3.0.4: dependencies: lru-cache "^5.1.1" -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +hosted-git-info@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" + integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== + dependencies: + lru-cache "^6.0.0" -htmlparser2@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" - integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== +htmlparser2@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" + integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== dependencies: domelementtype "^2.0.1" - domhandler "^3.0.0" - domutils "^2.0.0" + domhandler "^3.3.0" + domutils "^2.4.2" entities "^2.0.0" http-cache-semantics@3.8.1: @@ -5821,6 +5575,17 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-errors@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -5835,6 +5600,14 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" @@ -5848,6 +5621,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + husky@^4.2.5: version "4.2.5" resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.5.tgz#2b4f7622673a71579f901d9885ed448394b5fa36" @@ -5864,7 +5642,7 @@ husky@^4.2.5: slash "^3.0.0" which-pm-runs "^1.0.0" -iconv-lite@^0.4.24, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5885,6 +5663,11 @@ icss-utils@^4.0.0, icss-utils@^4.1.1: dependencies: postcss "^7.0.14" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ieee754@^1.1.4: version "1.1.13" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" @@ -5915,15 +5698,7 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.0.0, import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -5966,11 +5741,6 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" -indent-string@^3.0.0, indent-string@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -5981,7 +5751,7 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -infer-owner@^1.0.3, infer-owner@^1.0.4: +infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== @@ -5994,7 +5764,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6009,6 +5779,11 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -6033,25 +5808,6 @@ inquirer@^6.3.1: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^3.0.0" - cli-cursor "^3.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.15" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.5.3" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - insight@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/insight/-/insight-0.10.3.tgz#e7fa6593fd11323d227154cc8ce5f8bff638bf02" @@ -6089,13 +5845,6 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.2.2, invariant@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - dependencies: - loose-envify "^1.0.0" - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -6111,21 +5860,11 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -irregular-plurals@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" - integrity sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw== - irregular-plurals@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.2.0.tgz#b19c490a0723798db51b235d7e39add44dab0822" integrity sha512-YqTdPLfwP7YFN0SsD3QUVCkm9ZG2VzOXv3DOrw5G5mkMbVwptTwVcFv7/C0vOpBmgTxAeTG19XpUs1E522LW9Q== -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -6182,6 +5921,11 @@ is-callable@^1.1.4, is-callable@^1.1.5: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== +is-callable@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -6189,17 +5933,12 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= +is-core-module@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" + has "^1.0.3" is-data-descriptor@^0.1.4: version "0.1.4" @@ -6238,10 +5977,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= +is-docker@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" + integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== is-error@^2.2.0, is-error@^2.2.2: version "2.2.2" @@ -6270,6 +6009,13 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -6310,6 +6056,14 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-installed-globally@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -6339,11 +6093,21 @@ is-negated-glob@^1.0.0: resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + is-npm@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== +is-npm@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8" + integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA== + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -6411,10 +6175,10 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-plain-object@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" - integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== is-promise@^2.1.0: version "2.1.0" @@ -6441,6 +6205,14 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" +is-regex@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.1" + is-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" @@ -6448,11 +6220,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" @@ -6473,13 +6240,6 @@ is-string@^1.0.5: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -6514,6 +6274,13 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -6592,17 +6359,12 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - js-types@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/js-types/-/js-types-1.0.0.tgz#d242e6494ed572ad3c92809fc8bed7f7687cbf03" integrity sha1-0kLmSU7Vcq08koCfyL7X92h8vwM= -js-yaml@^3.10.0, js-yaml@^3.13.1: +js-yaml@^3.13.1: version "3.14.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== @@ -6610,6 +6372,14 @@ js-yaml@^3.10.0, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.14.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -6620,11 +6390,6 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -6640,7 +6405,12 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-typed@^7.0.1: +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A== @@ -6667,7 +6437,7 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0, json5@^2.1.1, json5@^2.1.2: +json5@^2.1.2, json5@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== @@ -6700,13 +6470,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jsx-ast-utils@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.3.0.tgz#edd727794ea284d7fda575015ed1b0cde0289ab6" - integrity sha512-3HNoc7nZ1hpZIKB3hJ7BlFRkzCx2BynRtfSwbkqZdpRdvAPsGMnzclPwrvDBS7/lalHTj21NwIeaEpysHBOudg== +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" + integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q== dependencies: - array-includes "^3.1.1" - object.assign "^4.1.0" + array-includes "^3.1.2" + object.assign "^4.1.2" just-extend@^4.0.2: version "4.1.0" @@ -6746,12 +6516,17 @@ kind-of@^5.0.0: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== -kind-of@^6.0.0, kind-of@^6.0.2: +kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -latest-version@^5.0.0: +klona@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== + +latest-version@^5.0.0, latest-version@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== @@ -6763,25 +6538,13 @@ lazy-val@^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== -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levenary@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77" - integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ== - dependencies: - leven "^3.1.0" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + prelude-ls "^1.2.1" + type-check "~0.4.0" line-column-path@^2.0.0: version "2.0.0" @@ -6790,6 +6553,14 @@ line-column-path@^2.0.0: dependencies: type-fest "^0.4.1" +line-column@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2" + integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI= + dependencies: + isarray "^1.0.0" + isobject "^2.0.0" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -6823,16 +6594,6 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - load-json-file@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" @@ -6867,7 +6628,7 @@ loader-utils@2.0.0, loader-utils@^2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^1.1.0, loader-utils@^1.2.3: +loader-utils@^1.2.3: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -6899,6 +6660,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -6914,21 +6682,11 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - lodash.zip@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" @@ -6944,12 +6702,17 @@ lodash@^4.17.19: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== +lodash@^4.17.20: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== dependencies: - chalk "^2.4.2" + chalk "^4.0.0" logalot@^2.0.0: version "2.1.0" @@ -6964,7 +6727,7 @@ longest@^1.0.0: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -7004,6 +6767,13 @@ lpad-align@^1.0.1: longest "^1.0.0" meow "^3.3.0" +lru-cache@6.0.0, lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -7061,10 +6831,10 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== -macos-release@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.0.tgz#837b39fc01785c3584f103c5599e0f0c8068b49e" - integrity sha512-ko6deozZYiAkqa/0gmcsz+p4jSy3gY7/ZsCEokPaYd8k+6/aXGkiTgr61+Owup7Sf+xjqW8u2ElhoM9SEcEfuA== +macos-release@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac" + integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg== macos-version@^5.0.0, macos-version@^5.2.0: version "5.2.0" @@ -7095,6 +6865,11 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + map-age-cleaner@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -7112,10 +6887,10 @@ map-obj@^1.0.0, map-obj@^1.0.1: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= -map-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" - integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= +map-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" + integrity sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g== map-visit@^1.0.0: version "1.0.0" @@ -7154,23 +6929,13 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdn-data@2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" - integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== - -mem@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-6.1.0.tgz#846eca0bd4708a8f04b9c3f3cd769e194ae63c5c" - integrity sha512-RlbnLQgRHk5lwqTtpEkBTQ2ll/CG/iB+J4Hy2Wh97PjgZgXgWJWrFF+XXujh3UUVLvR4OOTgZzcWMMwnehlEUg== +mem@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-8.0.0.tgz#b5e4b6d2d241c6296da05436173b4d0c7ae1f9ac" + integrity sha512-qrcJOe6uD+EW8Wrci1Vdiua/15Xw3n/QnaNXE7varnB6InxSk7nu3/i5jfy3S6kWxr8WYJ6R1o0afMUtvorTsA== dependencies: map-age-cleaner "^0.1.3" - mimic-fn "^3.0.0" + mimic-fn "^3.1.0" memory-fs@^0.2.0: version "0.2.0" @@ -7209,20 +6974,22 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" - integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== - dependencies: - camelcase-keys "^4.0.0" - decamelize-keys "^1.0.0" - loud-rejection "^1.0.0" - minimist-options "^3.0.1" - normalize-package-data "^2.3.4" - read-pkg-up "^3.0.0" - redent "^2.0.0" - trim-newlines "^2.0.0" - yargs-parser "^10.0.0" +meow@^8.1.0: + version "8.1.2" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" merge-stream@^2.0.0: version "2.0.0" @@ -7244,11 +7011,6 @@ micro-spelling-correcter@^1.1.1: resolved "https://registry.yarnpkg.com/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz#805a06a26ccfcad8f3e5c6a1ac5ff29d4530166e" integrity sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg== -microevent.ts@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" - integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== - micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -7311,25 +7073,30 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-fn@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b" - integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ== +mimic-fn@^3.0.0, mimic-fn@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== mimic-response@^1.0.0, mimic-response@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mini-css-extract-plugin@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.0.tgz#81d41ec4fe58c713a96ad7c723cdb2d0bd4d70e1" - integrity sha512-MNpRGbNA52q6U92i0qbVpQNsgk7LExy41MdAlG84FeytfDOtRIf/mCHdEgG8rpTKOaNKiqUnZdlptF469hxqOw== - dependencies: - loader-utils "^1.1.0" - normalize-url "1.9.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" @@ -7348,46 +7115,19 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist-options@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" - integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.3.tgz#55f7839307d74859d6e8ada9c3ebe72cec216a34" - integrity sha512-cFOknTvng5vqnwOpDsZTWhNll6Jf8o2x+/diplafmxpuIymAjzoOolZG0VvQf3V2HgqzJNhnuKHYp2BqDgz8IQ== - dependencies: - minipass "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== dependencies: - yallist "^4.0.0" + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mississippi@^3.0.0: version "3.0.0" @@ -7413,12 +7153,10 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.3: +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" - integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== - dependencies: - minimist "^1.2.5" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== mkdirp@^0.5.1, mkdirp@^0.5.4: version "0.5.4" @@ -7427,7 +7165,7 @@ mkdirp@^0.5.1, mkdirp@^0.5.4: dependencies: minimist "^1.2.5" -mkdirp@^0.5.3, mkdirp@~0.5.1: +mkdirp@^0.5.3: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -7468,12 +7206,17 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -ms@^2.1.1, ms@^2.1.2: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -multimap@^1.0.2: +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multimap@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multimap/-/multimap-1.1.0.tgz#5263febc085a1791c33b59bb3afc6a76a2a10ca8" integrity sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw== @@ -7483,16 +7226,16 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - nan@^2.12.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== +nanoid@^3.1.16: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -7510,10 +7253,15 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -native-url@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.1.tgz#5045c65d0eb4c3ee548d48e3cb50797eec5a3c54" - integrity sha512-VL0XRW8nNBdSpxqZCbLJKrLHmIMn82FZ8pJzriJgyBmErjdEtrUX6eZAJbtHjlkMooEWUV+EtJ0D5tOP3+1Piw== +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +native-url@0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.3.4.tgz#29c943172aed86c63cee62c8c04db7f5756661f8" + integrity sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA== dependencies: querystring "^0.2.0" @@ -7527,11 +7275,21 @@ nearest-normal-aspect-ratio@^1.2.1: resolved "https://registry.yarnpkg.com/nearest-normal-aspect-ratio/-/nearest-normal-aspect-ratio-1.2.1.tgz#64af91f2f2b1b99c2b4fda83f84d0eb4c3354f36" integrity sha512-Yi34fBfbffpLZXBmLBOcleAC1m256K8l/KM7ZvJLP6633cibYxbKjzgaewgZD482waUNfLqqq+DBaR/aG37VHA== -neo-async@2.6.1, neo-async@^2.5.0, neo-async@^2.6.1: +neo-async@^2.5.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nested-error-stacks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" + integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== + new-github-issue-url@^0.2.0, new-github-issue-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz#e17be1f665a92de465926603e44b9f8685630c1d" @@ -7542,62 +7300,59 @@ next-tick@~1.0.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= -next@^9.3.6: - version "9.4.2" - resolved "https://registry.yarnpkg.com/next/-/next-9.4.2.tgz#5d086aaaf9d683b457f615073dca39ed96a78cce" - integrity sha512-T/3Rmz/R++OyrWNZ3ZQn+RR9Tbu6DWxZGVXUc+2z4/FxIkeZXggB/494i3IsAssiK1Fu8WVDIwYxOWQ0RgITUg== - dependencies: - "@ampproject/toolbox-optimizer" "2.3.1" - "@babel/code-frame" "7.8.3" - "@babel/core" "7.7.7" - "@babel/plugin-proposal-class-properties" "7.8.3" - "@babel/plugin-proposal-nullish-coalescing-operator" "7.8.3" - "@babel/plugin-proposal-numeric-separator" "7.8.3" - "@babel/plugin-proposal-object-rest-spread" "7.9.6" - "@babel/plugin-proposal-optional-chaining" "7.9.0" - "@babel/plugin-syntax-bigint" "7.8.3" - "@babel/plugin-syntax-dynamic-import" "7.8.3" - "@babel/plugin-transform-modules-commonjs" "7.9.6" - "@babel/plugin-transform-runtime" "7.9.6" - "@babel/preset-env" "7.9.6" - "@babel/preset-modules" "0.1.3" - "@babel/preset-react" "7.9.4" - "@babel/preset-typescript" "7.9.0" - "@babel/runtime" "7.9.6" - "@babel/types" "7.9.6" - "@next/react-dev-overlay" "9.4.2" - "@next/react-refresh-utils" "9.4.2" - babel-plugin-syntax-jsx "6.18.0" +next@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/next/-/next-10.0.4.tgz#0d256f58a57d6bab7db7e533900c15f322960b4a" + integrity sha512-WXEYr1FuR2cLuWGN8peYGM6ykmbtwaHvrI6RqR2qrTXUNsW+KU5pzIMK5WPcpqP+xOuMhlykOCJvwJH8qU9FZQ== + dependencies: + "@ampproject/toolbox-optimizer" "2.7.1-alpha.0" + "@babel/runtime" "7.12.5" + "@hapi/accept" "5.0.1" + "@next/env" "10.0.4" + "@next/polyfill-module" "10.0.4" + "@next/react-dev-overlay" "10.0.4" + "@next/react-refresh-utils" "10.0.4" + "@opentelemetry/api" "0.14.0" + ast-types "0.13.2" babel-plugin-transform-define "2.0.0" babel-plugin-transform-react-remove-prop-types "0.4.24" - browserslist "4.12.0" - cacache "13.0.1" - chokidar "2.1.8" - css-loader "3.5.3" + browserslist "4.14.6" + buffer "5.6.0" + caniuse-lite "^1.0.30001113" + chalk "2.4.2" + chokidar "3.4.3" + crypto-browserify "3.12.0" + css-loader "4.3.0" + cssnano-simple "1.2.1" + etag "1.8.1" find-cache-dir "3.3.1" - fork-ts-checker-webpack-plugin "3.1.1" jest-worker "24.9.0" loader-utils "2.0.0" - mini-css-extract-plugin "0.8.0" - mkdirp "0.5.3" - native-url "0.3.1" - neo-async "2.6.1" + native-url "0.3.4" + node-fetch "2.6.1" + node-html-parser "1.4.9" + p-limit "3.1.0" + path-browserify "1.0.1" pnp-webpack-plugin "1.6.4" - postcss "7.0.29" + postcss "8.1.7" + process "0.11.10" prop-types "15.7.2" - prop-types-exact "1.2.0" + raw-body "2.4.1" react-is "16.13.1" - react-refresh "0.8.2" - resolve-url-loader "3.1.1" - sass-loader "8.0.2" - schema-utils "2.6.6" + react-refresh "0.8.3" + resolve-url-loader "3.1.2" + sass-loader "10.0.5" + schema-utils "2.7.1" + stream-browserify "3.0.0" style-loader "1.2.1" - styled-jsx "3.3.0" - use-subscription "1.4.1" + styled-jsx "3.3.2" + use-subscription "1.5.1" + vm-browserify "1.1.2" watchpack "2.0.0-beta.13" - web-vitals "0.2.1" - webpack "4.43.0" + webpack "4.44.1" webpack-sources "1.4.3" + optionalDependencies: + sharp "0.26.2" nice-try@^1.0.4: version "1.0.5" @@ -7615,10 +7370,29 @@ nise@^4.0.1: just-extend "^4.0.2" path-to-regexp "^1.7.0" -node-fetch@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" - integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== +node-abi@^2.7.0: + version "2.19.3" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d" + integrity sha512-9xZrlyfvKhWme2EXFKQhZRp1yNWT/uI1luYPr3sFl+H4keYY4xR+1jO7mvTTijIsHf1M+QDe9uWuKeEpLInIlg== + dependencies: + semver "^5.4.1" + +node-addon-api@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" + integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-html-parser@1.4.9: + version "1.4.9" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c" + integrity sha512-UVcirFD1Bn0O+TSmloHeHqZZCxHjvtIeGdVdGMhyZ8/PWlEiZaZ5iJzR189yKZr8p0FXN58BUeC7RHRkf/KYGw== + dependencies: + he "1.2.0" "node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1: version "2.2.1" @@ -7657,10 +7431,20 @@ node-mac-app-icon@^1.4.0: electron-util "^0.4.1" execa "^0.8.0" -node-releases@^1.1.53: - version "1.1.53" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" - integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== +node-releases@^1.1.65: + version "1.1.67" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12" + integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg== + +node-releases@^1.1.70: + version "1.1.71" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" + integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== + +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= normalize-html-whitespace@1.0.0: version "1.0.0" @@ -7677,6 +7461,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@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.0.tgz#1f8a7c423b3d2e85eb36985eaf81de381d01301a" + integrity sha512-6lUjEI0d3v6kFrtgA/lOx4zHCWULXsFNIjHolnZCKCTLA6m/G625cdn3O7eNmT0iD3jfo6HZ9cdImGZwf21prw== + dependencies: + hosted-git-info "^3.0.6" + resolve "^1.17.0" + semver "^7.3.2" + validate-npm-package-license "^3.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" @@ -7689,16 +7483,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - normalize-url@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" @@ -7708,11 +7492,6 @@ normalize-url@2.0.1: query-string "^5.0.1" sort-keys "^2.0.0" -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - normalize-url@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" @@ -7740,19 +7519,27 @@ npm-run-path@^3.0.0: dependencies: path-key "^3.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +npmlog@^4.0.1, npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: - boolbase "~1.0.0" + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= oauth-sign@~0.9.0: version "0.9.0" @@ -7778,21 +7565,26 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" + integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== +object-inspect@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" + integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== + object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-path@0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" - integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= - object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -7810,13 +7602,24 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.entries@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" - integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== dependencies: + call-bind "^1.0.0" define-properties "^1.1.3" - es-abstract "^1.17.5" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.entries@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6" + integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" has "^1.0.3" object.fromentries@^2.0.2: @@ -7844,7 +7647,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.1: +object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== @@ -7875,50 +7678,59 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open-editor@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/open-editor/-/open-editor-2.0.1.tgz#d001055770fbf6f6ee73c18f224915f444be863c" - integrity sha512-B3KdD7Pl8jYdpBSBBbdYaqVUI3whQjLl1G1+CvhNc8+d7GzKRUq+VuCIx1thxGiqD2oBGRvsZz7QWrBsFP2yVA== +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open-editor@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/open-editor/-/open-editor-3.0.0.tgz#2c9bfa013c0518601f0c321d89b68ff2bb6851ab" + integrity sha512-00Nqoa7k8F4AK1oSFMIIhYku+essXiCljR2L2kV+bl5j90ANgbQgzEeTdZu23LsikDoz+KfhyRHpGLAwpQhugA== dependencies: - env-editor "^0.4.0" + env-editor "^0.4.1" + execa "^5.0.0" line-column-path "^2.0.0" - open "^6.2.0" + open "^7.3.0" -open@^6.2.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== +open@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.0.tgz#ad95b98f871d9acb0ec8fecc557082cc9986626b" + integrity sha512-PGoBCX/lclIWlpS/R2PQuIR4NJoXh6X5AwVzE7WXnWRGvHg7+4TBCgsujUgiPpm0K1y4qvQeWnCWVTpTKZBtvA== dependencies: - is-wsl "^1.1.0" + is-docker "^2.0.0" + is-wsl "^2.1.1" opencollective-postinstall@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== -optionator@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" -ora@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d" - integrity sha512-77iGeVU1cIdRhgFzCK8aw1fbtT1B/iZAvWjS+l/o1x0RShMgxHUZaD2yDpWsNCPwXg9z1ZA78Kbdvr8kBmG/Ww== +ora@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f" + integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g== dependencies: - chalk "^3.0.0" + bl "^4.0.3" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-spinners "^2.2.0" + cli-spinners "^2.5.0" is-interactive "^1.0.0" - log-symbols "^3.0.0" - mute-stream "0.0.8" + log-symbols "^4.0.0" strip-ansi "^6.0.0" wcwidth "^1.0.1" @@ -8002,6 +7814,13 @@ p-event@^4.1.0: dependencies: p-timeout "^2.0.1" +p-event@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -8017,6 +7836,13 @@ p-is-promise@^1.1.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= +p-limit@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -8031,6 +7857,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.0.2.tgz#1664e010af3cadc681baafd3e2a437be7b0fb5fe" + integrity sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg== + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -8052,6 +7885,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -8059,13 +7899,6 @@ p-map-series@^1.0.0: dependencies: p-reduce "^1.0.0" -p-map@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" - integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== - dependencies: - aggregate-error "^3.0.0" - p-map@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" @@ -8105,7 +7938,7 @@ p-timeout@^2.0.1: dependencies: p-finally "^1.0.0" -p-timeout@^3.0.0: +p-timeout@^3.0.0, p-timeout@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== @@ -8153,6 +7986,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +park-miller@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/park-miller/-/park-miller-1.1.0.tgz#5831ca3b353166735e52a9c5fbb9ff8b1211b7c5" + integrity sha512-6mLXc2jkM9dcavPoxDHfof2QM/baCsrgK51iHbJDHl94AwymJv2Z/iGKQVwJMWUAXFiIV6FzY9UskjU3+KvLuA== + parse-asn1@^5.0.0, parse-asn1@^5.1.5: version "5.1.5" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e" @@ -8205,6 +8043,11 @@ path-browserify@0.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== +path-browserify@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -8371,7 +8214,7 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -pkg-up@^3.0.1: +pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== @@ -8399,13 +8242,6 @@ plist@^3.0.1: xmlbuilder "^9.0.7" xmldom "0.1.x" -plur@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/plur/-/plur-3.1.1.tgz#60267967866a8d811504fe58f2faaba237546a5b" - integrity sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w== - dependencies: - irregular-plurals "^2.0.0" - plur@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" @@ -8413,6 +8249,11 @@ plur@^4.0.0: dependencies: irregular-plurals "^3.2.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -8425,124 +8266,6 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-calc@^7.0.1: - version "7.0.2" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1" - integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - postcss-modules-extract-imports@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" @@ -8550,15 +8273,15 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz#e8a6561be914aaf3c052876377524ca90dbb7915" - integrity sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ== +postcss-modules-local-by-default@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== dependencies: icss-utils "^4.1.1" - postcss "^7.0.16" + postcss "^7.0.32" postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.0" + postcss-value-parser "^4.1.0" postcss-modules-scope@^2.2.0: version "2.2.0" @@ -8576,116 +8299,6 @@ postcss-modules-values@^3.0.0: icss-utils "^4.0.0" postcss "^7.0.6" -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - postcss-safe-parser@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" @@ -8693,15 +8306,6 @@ postcss-safe-parser@4.0.2: dependencies: postcss "^7.0.26" -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" @@ -8711,31 +8315,7 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.0.3: +postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -8749,16 +8329,26 @@ postcss@7.0.21: source-map "^0.6.1" supports-color "^6.1.0" -postcss@7.0.29: - version "7.0.29" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.29.tgz#d3a903872bd52280b83bce38cdc83ce55c06129e" - integrity sha512-ba0ApvR3LxGvRMMiUa9n0WR4HjzcYm7tS+ht4/2Nd0NLtHpPIH77fuB9Xh1/yJVz9O/E/95Y/dn8ygWsyffXtw== +postcss@7.0.32: + version "7.0.32" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" + integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== dependencies: chalk "^2.4.2" source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0.6: +postcss@8.1.7: + version "8.1.7" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.7.tgz#ff6a82691bd861f3354fd9b17b2332f88171233f" + integrity sha512-llCQW1Pz4MOPwbZLmOddGM9eIJ8Bh7SZ2Oj5sxZva77uVaotYDsYTch1WBTNu7fUY0fpWp0fdt7uW40D4sRiiQ== + dependencies: + colorette "^1.2.1" + line-column "^1.0.2" + nanoid "^3.1.16" + source-map "^0.6.1" + +postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.5, postcss@^7.0.6: version "7.0.27" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9" integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ== @@ -8767,21 +8357,42 @@ postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.26, postcss@^7.0. source-map "^0.6.1" supports-color "^6.1.0" -postcss@^7.0.1: - version "7.0.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.31.tgz#332af45cb73e26c0ee2614d7c7fb02dfcc2bd6dd" - integrity sha512-a937VDHE1ftkjk+8/7nj/mrjtmkn69xxzJgRETXdAUU+IgOYPQNJF17haGWbeDxSyk++HA14UA98FurvPyBJOA== +postcss@^7.0.32: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== dependencies: chalk "^2.4.2" source-map "^0.6.1" supports-color "^6.1.0" -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prebuild-install@^5.3.5: + version "5.3.6" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.6.tgz#7c225568d864c71d89d07f8796042733a3f54291" + integrity sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prepend-http@^1.0.0, prepend-http@^1.0.1: +prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= @@ -8798,10 +8409,15 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.4.tgz#2d1bae173e355996ee355ec9830a7a1ee05457ef" - integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w== +prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +pretty-bytes@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.5.0.tgz#0cecda50a74a941589498011cf23275aa82b339e" + integrity sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA== pretty-ms@^7.0.0: version "7.0.0" @@ -8810,17 +8426,19 @@ pretty-ms@^7.0.0: dependencies: parse-ms "^2.1.0" -private@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.10: +process@0.11.10, process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= @@ -8835,15 +8453,6 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= -prop-types-exact@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== - dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" - prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -8915,7 +8524,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: +pumpify@^1.3.3, pumpify@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== @@ -8946,24 +8555,18 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +pupa@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - query-string@^5.0.1: version "5.1.1" resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" @@ -8983,10 +8586,20 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -quick-lru@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" - integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +randoma@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/randoma/-/randoma-1.3.0.tgz#adf382394f15202947062a2ae02e81374e94acad" + integrity sha512-bi5f+w340fQ9LI4R4xYQd/YwrasOOZBPKNneSfZoCfwKYGFOVCNHilUKVzpzovS1iYY+NF/lsn5FW+EaxPBOPg== + dependencies: + "@sindresorhus/string-hash" "^1.2.0" + "@types/color" "^3.0.0" + color "^3.1.1" + park-miller "^1.1.0" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" @@ -9003,7 +8616,17 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -rc@^1.2.8: +raw-body@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -9037,10 +8660,10 @@ react-linkify@^0.2.2: prop-types "^15.5.8" tlds "^1.57.0" -react-refresh@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.2.tgz#24bb0858eac92b0d7b0dd561747f0c9fd6c60327" - integrity sha512-n8GXxo3DwM2KtFEL69DAVhGc4A1THn2qjmfvSo3nze0NLCoPbywazeJPqdp0RdSGLmyhQzeyA+XPXOobbYlkzg== +react-refresh@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" + integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== react@^16.13.1: version "16.13.1" @@ -9078,14 +8701,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - read-pkg-up@^7.0.0, read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -9113,15 +8728,6 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -9132,7 +8738,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -9145,7 +8751,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.6.0: +readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -9163,10 +8769,10 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== dependencies: picomatch "^2.2.1" @@ -9178,43 +8784,18 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -redent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" - integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= - dependencies: - indent-string "^3.0.0" - strip-indent "^2.0.0" - -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= - -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + indent-string "^4.0.0" + strip-indent "^3.0.0" regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== - -regenerator-transform@^0.14.2: - version "0.14.4" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7" - integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw== - dependencies: - "@babel/runtime" "^7.8.4" - private "^0.1.8" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" @@ -9224,10 +8805,10 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regex-parser@2.2.10: - version "2.2.10" - resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.10.tgz#9e66a8f73d89a107616e63b39d4deddfee912b37" - integrity sha512-8t6074A68gHfU8Neftl0Le6KTDwfGAj7IyjPIMSfikI2wJUTHDMaIq42bUsfVnj8mhx0R+45rdUXHGpN164avA== +regex-parser@^2.2.11: + version "2.2.11" + resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" + integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== regexp-tree@^0.1.21, regexp-tree@~0.1.1: version "0.1.21" @@ -9242,28 +8823,11 @@ regexp.prototype.flags@^1.3.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -regexpp@^3.0.0: +regexpp@^3.0.0, regexpp@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== -regexpu-core@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938" - integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - registry-auth-token@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479" @@ -9278,18 +8842,6 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -regjsgen@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" - integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== - -regjsparser@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272" - integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw== - dependencies: - jsesc "~0.5.0" - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -9343,6 +8895,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + 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" @@ -9360,11 +8917,6 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -9375,12 +8927,12 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url-loader@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.1.tgz#28931895fa1eab9be0647d3b2958c100ae3c0bf0" - integrity sha512-K1N5xUjj7v0l2j/3Sgs5b8CjrrgtC70SmdCuZiJ8tSyb5J+uk3FoeZ4b7yTnH6j7ngI+Bc5bldHJIa8hYdu2gQ== +resolve-url-loader@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08" + integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== dependencies: - adjust-sourcemap-loader "2.0.0" + adjust-sourcemap-loader "3.0.0" camelcase "5.3.1" compose-function "3.0.3" convert-source-map "1.7.0" @@ -9396,13 +8948,21 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.3.2, resolve@^1.8.1: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" +resolve@^1.18.1: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + responselike@1.0.2, responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -9449,31 +9009,14 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: +rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -9500,13 +9043,23 @@ roarr@^2.15.2: semver-compare "^1.0.0" sprintf-js "^1.1.2" -run-async@^2.2.0, run-async@^2.4.0: +run-async@^2.2.0: version "2.4.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== dependencies: is-promise "^2.1.0" +run-electron@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-electron/-/run-electron-1.0.0.tgz#cc67dd7dbfd13c3ae5d9795d437b118722215cd7" + integrity sha512-kkiy7vyuSIxtjNKexGHu+or/A5AIduPgYmJtZrj8vZbvzG3JLcbsfvAri+ySCgfo+vKyTXegZ8Xpco5/qQhjVA== + dependencies: + pumpify "^1.5.1" + split2 "^3.1.1" + through2-filter "^3.0.0" + through2-map "^3.0.0" + run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -9519,7 +9072,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.5.3: +rxjs@^6.4.0: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== @@ -9562,18 +9115,18 @@ sanitize-filename@^1.6.2, sanitize-filename@^1.6.3: dependencies: truncate-utf8-bytes "^1.0.0" -sass-loader@8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-8.0.2.tgz#debecd8c3ce243c76454f2e8290482150380090d" - integrity sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ== +sass-loader@10.0.5: + version "10.0.5" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.5.tgz#f53505b5ddbedf43797470ceb34066ded82bb769" + integrity sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w== dependencies: - clone-deep "^4.0.1" - loader-utils "^1.2.3" - neo-async "^2.6.1" - schema-utils "^2.6.1" - semver "^6.3.0" + klona "^2.0.4" + loader-utils "^2.0.0" + neo-async "^2.6.2" + schema-utils "^3.0.0" + semver "^7.3.2" -sax@^1.2.4, sax@~1.2.4: +sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -9586,13 +9139,14 @@ scheduler@^0.19.1: loose-envify "^1.1.0" object-assign "^4.1.1" -schema-utils@2.6.6, schema-utils@^2.6.6: - version "2.6.6" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.6.tgz#299fe6bd4a3365dc23d99fd446caff8f1d6c330c" - integrity sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA== +schema-utils@2.7.1, schema-utils@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== dependencies: - ajv "^6.12.0" - ajv-keywords "^3.4.1" + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" schema-utils@^1.0.0: version "1.0.0" @@ -9603,14 +9157,23 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.1: - version "2.6.5" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a" - integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ== +schema-utils@^2.6.6: + version "2.6.6" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.6.tgz#299fe6bd4a3365dc23d99fd446caff8f1d6c330c" + integrity sha512-wHutF/WPSbIi9x6ctjGGk2Hvl0VOz5l3EKEuKbjPlB30mKZUzb9A5k9yEXRX3pwyqVLPvpfZZEllaFq/M718hA== dependencies: ajv "^6.12.0" ajv-keywords "^3.4.1" +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + seek-bzip@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" @@ -9642,30 +9205,27 @@ semver-truncate@^1.1.2: dependencies: semver "^5.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.1.2, semver@^7.1.3, semver@^7.3.2: +semver@^7.1.2, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -serialize-error@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" - integrity sha1-ULZ51WNc34Rme9yOWa9OW4HV9go= +semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" serialize-error@^5.0.0: version "5.0.0" @@ -9674,12 +9234,19 @@ serialize-error@^5.0.0: dependencies: type-fest "^0.8.0" +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -9699,6 +9266,11 @@ setimmediate@^1.0.4: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" @@ -9707,12 +9279,20 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== +sharp@0.26.2: + version "0.26.2" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.26.2.tgz#3d5777d246ae32890afe82a783c1cbb98456a88c" + integrity sha512-bGBPCxRAvdK9bX5HokqEYma4j/Q5+w8Nrmb2/sfgQCLEUx/HblcpmOfp59obL3+knIKnOhyKmDb4tEOhvFlp6Q== dependencies: - kind-of "^6.0.2" + color "^3.1.2" + detect-libc "^1.0.3" + node-addon-api "^3.0.2" + npmlog "^4.1.2" + prebuild-install "^5.3.5" + semver "^7.3.2" + simple-get "^4.0.0" + tar-fs "^2.1.0" + tunnel-agent "^0.6.0" shebang-command@^1.2.0: version "1.2.0" @@ -9751,11 +9331,34 @@ side-channel@^1.0.2: es-abstract "^1.17.0-next.1" object-inspect "^1.7.0" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-get@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" + integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -9786,15 +9389,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -9804,6 +9398,15 @@ slice-ansi@^3.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -9871,7 +9474,7 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.19, source-map-support@~0.5.12: +source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -9889,7 +9492,7 @@ source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@0.7.3: +source-map@0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -9939,6 +9542,20 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +split2@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" + integrity sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw== + dependencies: + through2 "^2.0.2" + +split2@^3.1.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" @@ -9980,23 +9597,10 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -ssri@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-7.1.0.tgz#92c241bf6de82365b5c7fb4bd76e975522e1294d" - integrity sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g== - dependencies: - figgy-pudding "^3.5.1" - minipass "^3.1.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -stack-utils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" - integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== +stack-utils@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277" + integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw== dependencies: escape-string-regexp "^2.0.0" @@ -10020,6 +9624,19 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.5.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-browserify@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" + integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== + dependencies: + inherits "~2.0.4" + readable-stream "^3.5.0" + stream-browserify@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" @@ -10067,7 +9684,16 @@ string-math@^1.2.1: resolved "https://registry.yarnpkg.com/string-math/-/string-math-1.2.1.tgz#3abe7ce4225ee14f8ddf10037e7a44ab2aa79c1c" integrity sha512-r5ouWjy+qzWW4UsKSa8Annd5w6n1Z+7LyN3NySTW5UDBG9oeM3aq2+IL7yy+XkCpQIaEK8AcDyIdwI2B2td5Aw== -string-width@^2.1.0: +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -10113,6 +9739,14 @@ string.prototype.trimend@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" +string.prototype.trimend@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" + integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + string.prototype.trimleft@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" @@ -10139,6 +9773,14 @@ string.prototype.trimstart@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" +string.prototype.trimstart@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" + integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -10160,7 +9802,7 @@ strip-ansi@6.0.0, strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^3.0.0: +strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= @@ -10174,7 +9816,7 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.1.0, strip-ansi@^5.2.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== @@ -10217,15 +9859,17 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" -strip-json-comments@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-json-comments@~2.0.1: version "2.0.1" @@ -10247,10 +9891,10 @@ style-loader@1.2.1: loader-utils "^2.0.0" schema-utils "^2.6.6" -styled-jsx@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.0.tgz#32335c1a3ecfc923ba4f9c056eeb3d4699006b09" - integrity sha512-sh8BI5eGKyJlwL4kNXHjb27/a/GJV8wP4ElRIkRXrGW3sHKOsY9Pa1VZRNxyvf3+lisdPwizD9JDkzVO9uGwZw== +styled-jsx@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-3.3.2.tgz#2474601a26670a6049fb4d3f94bd91695b3ce018" + integrity sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g== dependencies: "@babel/types" "7.8.3" babel-plugin-syntax-jsx "6.18.0" @@ -10261,15 +9905,6 @@ styled-jsx@3.3.0: stylis "3.5.4" stylis-rule-sheet "0.0.10" -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - stylis-rule-sheet@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" @@ -10287,16 +9922,16 @@ sumchecker@^3.0.1: dependencies: debug "^4.1.0" -supertap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supertap/-/supertap-1.0.0.tgz#bd9751c7fafd68c68cf8222a29892206a119fa9e" - integrity sha512-HZJ3geIMPgVwKk2VsmO5YHqnnJYl6bV5A9JW2uzqV43WmpgliNEYbuvukfor7URpaqpxuw3CfZ3ONdVbZjCgIA== +supertap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supertap/-/supertap-2.0.0.tgz#8b587d6e14b8e885fa5183a9c45abf429feb9f7f" + integrity sha512-jRzcXlCeDYvKoZGA5oRhYyR3jUIYu0enkSxtmAgHRlD7HwrovTpH4bDSi0py9FtuA8si9cW/fKommJHuaoDHJA== dependencies: - arrify "^1.0.1" - indent-string "^3.2.0" - js-yaml "^3.10.0" - serialize-error "^2.1.0" - strip-ansi "^4.0.0" + arrify "^2.0.1" + indent-string "^4.0.0" + js-yaml "^3.14.0" + serialize-error "^7.0.1" + strip-ansi "^6.0.0" supports-color@^2.0.0: version "2.0.0" @@ -10332,34 +9967,15 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" -svgo@^1.0.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== +table@^6.0.4: + version "6.0.7" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" + integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" + ajv "^7.0.2" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" tapable@^0.1.8: version "0.1.10" @@ -10371,6 +9987,16 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tar-fs@^2.0.0, tar-fs@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" @@ -10384,6 +10010,17 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" @@ -10448,14 +10085,14 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@4.6.13: - version "4.6.13" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.13.tgz#e879a7364a5e0db52ba4891ecde007422c56a916" - integrity sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw== +terser@5.5.1: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== dependencies: commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map "~0.7.2" + source-map-support "~0.5.19" terser@^4.1.2: version "4.6.9" @@ -10471,7 +10108,23 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -through2@^2.0.0: +through2-filter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" + integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== + dependencies: + through2 "~2.0.0" + xtend "~4.0.0" + +through2-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/through2-map/-/through2-map-3.0.0.tgz#a6c3026ce63b4898a997d540506b66ffd970f271" + integrity sha1-psMCbOY7SJipl9VAUGtm/9lw8nE= + dependencies: + through2 "~2.0.0" + xtend "^4.0.0" + +through2@^2.0.0, through2@^2.0.2, through2@~2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -10506,11 +10159,6 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - tlds@^1.57.0: version "1.207.0" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.207.0.tgz#459264e644cf63ddc0965fece3898913286b1afd" @@ -10590,6 +10238,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + tough-cookie@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" @@ -10624,10 +10277,10 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= -trim-newlines@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" - integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= +trim-newlines@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" + integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== trim-off-newlines@^1.0.1: version "1.0.1" @@ -10648,11 +10301,33 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +ts-node@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" + integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== + dependencies: + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" @@ -10687,12 +10362,12 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: - prelude-ls "~1.1.2" + prelude-ls "^1.2.1" type-detect@4.0.8, type-detect@^4.0.8: version "4.0.8" @@ -10709,6 +10384,26 @@ type-fest@^0.12.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.0.tgz#a94413e6145d1c261ae680f1d5d010746c75164c" + integrity sha512-1l9DXlbflV880ZijiK9qx4jdD0VOqogKx5i33t3hDN+ZiaqMOr7aSwH/jzmnBXPQon+SNvr+cH6wltATEzGJEg== + type-fest@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -10744,6 +10439,11 @@ type@^2.0.0: resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== +typed-emitter@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/typed-emitter/-/typed-emitter-1.3.1.tgz#c98d71551a99d5f08ba9085ee44b8fc9b2357502" + integrity sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -10756,10 +10456,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.0.0: - version "3.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" - integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== +typescript@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== + +typescript@^4.1.3: + version "4.1.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.5.tgz#123a3b214aaff3be32926f0d8f1f6e704eb89a72" + integrity sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA== uc.micro@^1.0.1: version "1.0.6" @@ -10779,29 +10484,6 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -10817,11 +10499,6 @@ uniq@^1.0.1: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -10860,10 +10537,10 @@ universalify@^1.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d" integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug== -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" @@ -10873,6 +10550,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +unstated-next@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unstated-next/-/unstated-next-1.1.0.tgz#7bb4911a12fdf3cc8ad3eb11a0b315e4a8685ea8" + integrity sha512-AAn47ZncPvgBGOvMcn8tSRxsrqwf2VdAPxLASTuLJvZt4rhKfDvUkmYZLGfclImSfTVMv7tF4ynaVxin0JjDCA== + unstated@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/unstated/-/unstated-1.2.0.tgz#5c57cc077473d2cce411ec0930da285cef3df306" @@ -10885,7 +10567,7 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-notifier@^4.0.0, update-notifier@^4.1.0: +update-notifier@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== @@ -10904,6 +10586,26 @@ update-notifier@^4.0.0, update-notifier@^4.1.0: semver-diff "^3.1.1" xdg-basedir "^4.0.0" +update-notifier@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9" + integrity sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw== + dependencies: + boxen "^5.0.0" + chalk "^4.1.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.4.0" + is-npm "^5.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.1.0" + pupa "^2.1.1" + semver "^7.3.4" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -10943,10 +10645,10 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" -use-subscription@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.4.1.tgz#edcbcc220f1adb2dd4fa0b2f61b6cc308e620069" - integrity sha512-7+IIwDG/4JICrWHL/Q/ZPK5yozEnvRm6vHImu0LKwQlmWGKeiF7mbAenLlK/cTNXrTtXHU/SFASQHzB6+oSJMQ== +use-subscription@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== dependencies: object-assign "^4.1.1" @@ -10965,7 +10667,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util.promisify@1.0.1, util.promisify@~1.0.0: +util.promisify@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== @@ -11012,11 +10714,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -11026,7 +10723,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vm-browserify@^1.0.1: +vm-browserify@1.1.2, vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== @@ -11046,15 +10743,15 @@ watchpack@2.0.0-beta.13: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -watchpack@^1.6.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== +watchpack@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" + integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== dependencies: graceful-fs "^4.1.2" neo-async "^2.5.0" optionalDependencies: - chokidar "^3.4.0" + chokidar "^3.4.1" watchpack-chokidar2 "^2.0.0" wcwidth@^1.0.1: @@ -11064,17 +10761,12 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-vitals@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-0.2.1.tgz#60782fa690243fe35613759a0c26431f57ba7b2d" - integrity sha512-2pdRlp6gJpOCg0oMMqwFF0axjk5D9WInc09RSYtqFgPXQ15+YKNQ7YnBBEqAL5jvmfH9WvoXDMb8DHwux7pIew== - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== -webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@1.4.3, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== @@ -11082,10 +10774,10 @@ webpack-sources@1.4.3, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-s source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.43.0: - version "4.43.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.43.0.tgz#c48547b11d563224c561dad1172c8aa0b8a678e6" - integrity sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g== +webpack@4.44.1: + version "4.44.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" + integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -11095,7 +10787,7 @@ webpack@4.43.0: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" + enhanced-resolve "^4.3.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" @@ -11108,7 +10800,7 @@ webpack@4.43.0: schema-utils "^1.0.0" tapable "^1.1.3" terser-webpack-plugin "^1.4.3" - watchpack "^1.6.1" + watchpack "^1.7.4" webpack-sources "^1.4.1" well-known-symbols@^2.0.0: @@ -11116,11 +10808,6 @@ well-known-symbols@^2.0.0: resolved "https://registry.yarnpkg.com/well-known-symbols/-/well-known-symbols-2.0.0.tgz#e9c7c07dbd132b7b84212c8174391ec1f9871ba5" integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== -whatwg-fetch@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" - integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== - whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -11154,6 +10841,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + widest-line@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" @@ -11168,7 +10862,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -11180,13 +10874,6 @@ worker-farm@^1.7.0: dependencies: errno "~0.1.7" -worker-rpc@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" - integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== - dependencies: - microevent.ts "~0.1.1" - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -11196,6 +10883,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -11220,13 +10916,6 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -11242,63 +10931,56 @@ xmldom@0.1.x: resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== -xo@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/xo/-/xo-0.30.0.tgz#e2991da22dea4c08c192cf74bba66cb634528ed2" - integrity sha512-755dmXTIj8uHv0lUZmDrtQb98wdu6ryqgcy8jvXBZgABz+kHm12IlzRQP+bO+w1u6NqkQB0Nn0Bh6Uv+KhdYiA== +xo@^0.37.1: + version "0.37.1" + resolved "https://registry.yarnpkg.com/xo/-/xo-0.37.1.tgz#0843b6a7c6193fa85ae23d26e44eb1fc5652057b" + integrity sha512-UsD1QkITsojNJmiYaDg20GOuTEuHAYtys6Cz4JVWbral6hbCb+WU8ktt9xCTDrsr2rNPjoxp1oonoz9HkPYGnQ== dependencies: - "@typescript-eslint/eslint-plugin" "^2.29.0" - "@typescript-eslint/parser" "^2.29.0" + "@typescript-eslint/eslint-plugin" "^4.11.1" + "@typescript-eslint/parser" "^4.11.1" arrify "^2.0.1" - cosmiconfig "^6.0.0" - debug "^4.1.0" - eslint "^6.8.0" - eslint-config-prettier "^6.11.0" - eslint-config-xo "^0.29.0" - eslint-config-xo-typescript "^0.28.0" - eslint-formatter-pretty "^3.0.1" - eslint-import-resolver-webpack "^0.12.1" - eslint-plugin-ava "^10.0.1" - eslint-plugin-eslint-comments "^3.1.2" - eslint-plugin-import "^2.20.1" + cosmiconfig "^7.0.0" + debug "^4.3.1" + eslint "^7.17.0" + eslint-config-prettier "^7.1.0" + eslint-config-xo "^0.34.0" + eslint-config-xo-typescript "^0.37.0" + eslint-formatter-pretty "^4.0.0" + eslint-import-resolver-webpack "^0.13.0" + eslint-plugin-ava "^11.0.0" + eslint-plugin-eslint-comments "^3.2.0" + eslint-plugin-import "^2.22.1" eslint-plugin-no-use-extend-native "^0.5.0" - eslint-plugin-node "^11.0.0" - eslint-plugin-prettier "^3.1.3" + eslint-plugin-node "^11.1.0" + eslint-plugin-prettier "^3.3.0" eslint-plugin-promise "^4.2.1" - eslint-plugin-unicorn "^19.0.0" + eslint-plugin-unicorn "^25.0.1" find-cache-dir "^3.3.1" - find-up "^4.1.0" - fs-extra "^9.0.0" - get-stdin "^7.0.0" - globby "^9.0.0" + find-up "^5.0.0" + fs-extra "^9.0.1" + get-stdin "^8.0.0" + globby "^9.2.0" has-flag "^4.0.0" imurmurhash "^0.1.4" is-path-inside "^3.0.2" json-stable-stringify-without-jsonify "^1.0.1" - json5 "^2.1.1" - lodash "^4.17.15" - meow "^5.0.0" + json5 "^2.1.3" + lodash "^4.17.20" + meow "^8.1.0" micromatch "^4.0.2" - open-editor "^2.0.1" + open-editor "^3.0.0" p-reduce "^2.1.0" path-exists "^4.0.0" - prettier "2.0.4" + prettier "^2.2.1" resolve-cwd "^3.0.0" resolve-from "^5.0.0" - semver "^7.3.2" + semver "^7.3.4" slash "^3.0.0" to-absolute-glob "^2.0.2" - typescript "^3.0.0" - update-notifier "^4.0.0" - -xregexp@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" - integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== - dependencies: - "@babel/runtime-corejs3" "^7.8.3" + typescript "^4.1.3" + update-notifier "^5.0.1" -xtend@^4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -11308,6 +10990,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -11323,6 +11010,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + yaml@^1.7.2: version "1.8.3" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" @@ -11330,13 +11022,6 @@ yaml@^1.7.2: dependencies: "@babel/runtime" "^7.8.7" -yargs-parser@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" - integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== - dependencies: - camelcase "^4.1.0" - yargs-parser@^18.1.1: version "18.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" @@ -11345,6 +11030,11 @@ yargs-parser@^18.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.2, yargs-parser@^20.2.3: + version "20.2.5" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.5.tgz#5d37729146d3f894f39fc94b6796f5b239513186" + integrity sha512-jYRGS3zWy20NtDtK2kBgo/TlAoy5YUuhD9/LZ7z7W4j1Fdw2cqD0xEEclf8fxc8xjD6X5Qr+qQQwCEsP8iRiYg== + yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" @@ -11362,6 +11052,19 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.1" +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yarn@^1.22.4: version "1.22.4" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.4.tgz#01c1197ca5b27f21edc8bc472cd4c8ce0e5a470e" @@ -11374,3 +11077,13 @@ yauzl@^2.10.0, yauzl@^2.4.2: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==