Merge pull request #588 from hashicorp/frameless-desktop-shell-on-windows-os
Use frameless desktop shell on Windows OS
This commit is contained in:
19 files changed
+223
-174
No files matched your search
@@ -60,6 +60,28 @@ export default function initializeMockIPC(server) {
|
||||
session_id: newSession.id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for browsers running on mac OS
|
||||
*/
|
||||
isMacOS() {
|
||||
return Boolean(window.navigator.userAgent.match(/(macintosh)/i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for browsers running on windows OS
|
||||
*/
|
||||
isWindowsOS() {
|
||||
return Boolean(window.navigator.userAgent.match(/(windows)/i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing when attempting to minimize, toggle fullscreen,
|
||||
* and close a browser window
|
||||
*/
|
||||
minimizeWindow() {}
|
||||
closeWindow() {}
|
||||
toggleFullScreenWindow() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
Canonical reference for changes, improvements, and bugfixes for Boundary Desktop.
|
||||
|
||||
## Next
|
||||
|
||||
### New
|
||||
|
||||
- Add Windows 10 support for client. ([PR](https://github.com/hashicorp/boundary-ui/pull/581))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Client will show update available prompt when a new version is released. ([PR](https://github.com/hashicorp/boundary-ui/pull/566))
|
||||
|
||||
## v1.0.1 [CLI 0.2.2] (2021.05.19)
|
||||
|
||||
### New
|
||||
|
||||
@@ -43,6 +43,14 @@ export default class ApplicationRoute extends Route {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add OS config on controller
|
||||
*/
|
||||
async setupController(controller) {
|
||||
controller.set('isMacOS', await this.ipc.invoke('isMacOS'));
|
||||
controller.set('isWindowsOS', await this.ipc.invoke('isWindowsOS'));
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
@@ -116,4 +124,19 @@ export default class ApplicationRoute extends Route {
|
||||
rootEl.classList.remove('rose-theme-light');
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
minimize() {
|
||||
this.ipc.invoke('minimizeWindow');
|
||||
}
|
||||
|
||||
@action
|
||||
toggleFullScreen() {
|
||||
this.ipc.invoke('toggleFullscreenWindow');
|
||||
}
|
||||
|
||||
@action
|
||||
close() {
|
||||
this.ipc.invoke('closeWindow');
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,33 @@
|
||||
|
||||
.rose-layout-global-body {
|
||||
max-height: 100vh;
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
|
||||
main {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.rose-layout-sidebar-sidebar,
|
||||
.rose-layout-sidebar-body {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
&.layout-scroll {
|
||||
.rose-layout-global-body {
|
||||
overflow-y: auto;
|
||||
|
||||
main {
|
||||
overflow: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.rose-layout-sidebar-sidebar,
|
||||
.rose-layout-sidebar-body {
|
||||
overflow-y: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rose-nav-sidebar {
|
||||
@@ -121,8 +141,28 @@
|
||||
.rose-header {
|
||||
-webkit-app-region: drag;
|
||||
min-height: sizing.rems(l) * 2;
|
||||
padding-left: sizing.rems(l) * 3;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
|
||||
.rose-header-brand,
|
||||
.rose-header-nav,
|
||||
.rose-header-utilities .rose-dropdown,
|
||||
.rose-header-utilities button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
&.header-cushion {
|
||||
padding-left: sizing.rems(l) * 3;
|
||||
}
|
||||
|
||||
.button-window-minimize,
|
||||
.button-window-fullscreen,
|
||||
.button-window-close {
|
||||
background: transparent;
|
||||
border: transparent;
|
||||
color: var(--white);
|
||||
margin: sizing.rems(xs);
|
||||
}
|
||||
|
||||
.logotype {
|
||||
display: none;
|
||||
@@ -214,13 +254,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rose-layout-global .rose-layout-global-body {
|
||||
main {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.branded-card {
|
||||
border: 1px solid var(--ui-border-subtler-1);
|
||||
border-radius: sizing.rems(xxxs);
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
{{page-title (app-name)}}
|
||||
|
||||
<Rose::Layout::Global as |layout|>
|
||||
<Rose::Layout::Global
|
||||
class='{{if this.isWindowsOS ' layout-scroll'}}'
|
||||
as |layout|
|
||||
>
|
||||
|
||||
<layout.header>
|
||||
<Rose::Header class='{{if (is-loading) ' loading'}}' as |header|>
|
||||
<Rose::Header
|
||||
class='{{if (is-loading) ' loading'}}
|
||||
{{if this.isMacOS ' header-cushion'}}'
|
||||
as |header|
|
||||
>
|
||||
<header.brand @logo='logo-app' @text={{app-name}} />
|
||||
|
||||
{{#if this.session.isAuthenticated}}
|
||||
<header.nav>
|
||||
{{outlet 'header-nav'}}
|
||||
</header.nav>
|
||||
{{/if}}
|
||||
|
||||
<header.utilities as |utilities|>
|
||||
<header.utilities as |utilities|>
|
||||
{{#if this.isWindowsOS}}
|
||||
<Rose::Button
|
||||
@iconOnly='cancel-plain'
|
||||
@style='warning'
|
||||
class='button-window-close'
|
||||
{{on 'click' (route-action 'close')}}
|
||||
/>
|
||||
<Rose::Button
|
||||
@iconOnly='box-outline'
|
||||
@style='secondary'
|
||||
class='button-window-fullscreen'
|
||||
{{on 'click' (route-action 'toggleFullScreen')}}
|
||||
/>
|
||||
<Rose::Button
|
||||
@iconOnly='minus-plain'
|
||||
@style='secondary'
|
||||
class='button-window-minimize'
|
||||
{{on 'click' (route-action 'minimize')}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.session.isAuthenticated}}
|
||||
<utilities.dropdown
|
||||
@text={{this.session.data.authenticated.username}}
|
||||
@icon='app-icons/user-profile'
|
||||
@@ -49,11 +78,10 @@
|
||||
</form.radioGroup>
|
||||
</Rose::Form>
|
||||
</dropdown.section>
|
||||
|
||||
</utilities.dropdown>
|
||||
</header.utilities>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</header.utilities>
|
||||
</Rose::Header>
|
||||
</layout.header>
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"lookpath": "^1.1.0",
|
||||
"node-html-parser": "^3.1.3",
|
||||
"semver": "^7.3.5",
|
||||
"shell-quote": "^1.7.2"
|
||||
"shell-quote": "^1.7.2",
|
||||
"tree-kill": "^1.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "6.0.0-beta.54",
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
const path = require('path');
|
||||
const { isWindows } = require('../helpers/platform.js');
|
||||
const { spawnSync } = require('../helpers/spawn-promise.js');
|
||||
|
||||
const cliPath = async () => {
|
||||
let name = 'boundary';
|
||||
if (isWindows()) name = 'boundary.exe';
|
||||
return path.resolve(__dirname, '..', 'cli', name);
|
||||
};
|
||||
const { path } = require('./path.js');
|
||||
|
||||
module.exports = {
|
||||
// Check boundary cli existence
|
||||
exists: () => Boolean(cliPath()),
|
||||
exists: () => Boolean(path()),
|
||||
|
||||
// Returns JSON-formatted version information from the CLI
|
||||
version: () => {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const path = require('path');
|
||||
const { isWindows } = require('../helpers/platform.js');
|
||||
|
||||
module.exports = {
|
||||
// Returns boundary cli path
|
||||
path: () => {
|
||||
const name = isWindows() ? 'boundary.exe' : 'boundary';
|
||||
return path.resolve(__dirname, '..', '..', 'cli', name);
|
||||
}
|
||||
};
|
||||
@@ -4,12 +4,12 @@ const https = require('https');
|
||||
const semver = require('semver');
|
||||
const { parse } = require('node-html-parser');
|
||||
const { autoUpdater, dialog } = require('electron');
|
||||
// TODO: Pickup app release version from config file
|
||||
// const { releaseVersion } = require('../config/forge.config.js');
|
||||
const { isWindows } = require('../helpers/platform.js');
|
||||
const config = require('../../config/config.js');
|
||||
|
||||
let currentVersion = config.releaseVersion;
|
||||
const debug = process.env.DEBUG_APP_UPDATER;
|
||||
const releasesUrl = 'https://releases.hashicorp.com/boundary-desktop/';
|
||||
let currentVersion = '1.0.0';
|
||||
if (debug && process.env.APP_UPDATER_CURRENT_VERSION) {
|
||||
currentVersion = process.env.APP_UPDATER_CURRENT_VERSION;
|
||||
}
|
||||
@@ -124,6 +124,9 @@ const displayDownloadPrompt = (version, url) => {
|
||||
**/
|
||||
module.exports = {
|
||||
run: async ({ suppressNoUpdatePrompt } = {}) => {
|
||||
// Do nothing on Windows - yet
|
||||
if (isWindows()) return;
|
||||
|
||||
let latestVersion;
|
||||
if (debug) {
|
||||
latestVersion = process.env.APP_UPDATER_LATEST_VERSION_TAG;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
const path = require('path');
|
||||
const { path } = require('../cli/path.js');
|
||||
const { spawn, spawnSync } = require('child_process');
|
||||
const jsonify = require('../utils/jsonify.js');
|
||||
|
||||
const boundaryPath = path.resolve(__dirname, '..', '..', 'cli', 'boundary');
|
||||
const { isWindows } = require('../helpers/platform.js');
|
||||
|
||||
// You can throw exceptions, or allow them to occur, and this is supported.
|
||||
// Exceptions thrown in this way will be returned to the UI as an
|
||||
@@ -29,7 +28,7 @@ module.exports = {
|
||||
*/
|
||||
spawnAsyncJSONPromise(command) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const childProcess = spawn(boundaryPath, command);
|
||||
const childProcess = spawn(path(), command, { shell: isWindows() });
|
||||
let outputStream = '';
|
||||
let errorStream = '';
|
||||
|
||||
@@ -56,6 +55,7 @@ module.exports = {
|
||||
};
|
||||
|
||||
childProcess.stderr.on('error', processError);
|
||||
childProcess.stderr.on('data', processError);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -66,7 +66,7 @@ module.exports = {
|
||||
* @return {string}
|
||||
*/
|
||||
spawnSync(command) {
|
||||
const childProcess = spawnSync(boundaryPath, command);
|
||||
const childProcess = spawnSync(path(), command, { shell: isWindows() });
|
||||
const rawOutput = childProcess.output.toString();
|
||||
return rawOutput;
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@ const sessionManager = require('./services/session-manager.js');
|
||||
|
||||
const menu = require('./config/menu.js');
|
||||
const appUpdater = require('./helpers/app-updater.js');
|
||||
const { isWindows } = require('./helpers/platform.js');
|
||||
const isDev = require('electron-is-dev');
|
||||
|
||||
// Register the custom file protocol
|
||||
@@ -115,6 +116,7 @@ app.on('ready', async () => {
|
||||
width: 1280,
|
||||
height: 760,
|
||||
titleBarStyle: 'hiddenInset',
|
||||
frame: !isWindows(),
|
||||
webPreferences: {
|
||||
partition,
|
||||
sandbox: true,
|
||||
@@ -195,16 +197,8 @@ app.on('before-quit', (event) => {
|
||||
detail: 'Close sessions before quitting?',
|
||||
};
|
||||
|
||||
dialog.showMessageBox(dialogOpts).then((returnValue) => {
|
||||
switch (returnValue.response) {
|
||||
case 0:
|
||||
sessionManager.stopAll();
|
||||
break;
|
||||
default:
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
});
|
||||
const buttonId = dialog.showMessageBoxSync(null, dialogOpts);
|
||||
buttonId === 0 ? sessionManager.stopAll() : event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { shell } = require('electron');
|
||||
const { app, shell, BrowserWindow } = require('electron');
|
||||
const isDev = require('electron-is-dev');
|
||||
const handle = require('./ipc-handler.js');
|
||||
const boundaryCli = require('../cli/index.js');
|
||||
const sessionManager = require('../services/session-manager.js');
|
||||
const runtimeSettings = require('../services/runtime-settings.js');
|
||||
const sanitizer = require('../utils/sanitizer.js');
|
||||
const { isMac, isWindows } = require('../helpers/platform.js');
|
||||
|
||||
/**
|
||||
* Returns the current runtime origin, which is used by the main thread to
|
||||
@@ -64,3 +65,31 @@ handle('connect', ({ target_id, token, host_id }) =>
|
||||
* Cancel an established boundary session spawned process.
|
||||
*/
|
||||
handle('stop', ({ session_id }) => sessionManager.stopById(session_id));
|
||||
|
||||
/**
|
||||
* Check for MacOS OS
|
||||
*/
|
||||
handle('isMacOS', () => isMac());
|
||||
|
||||
/**
|
||||
* Check for Windows OS
|
||||
*/
|
||||
handle('isWindowsOS', () => isWindows());
|
||||
|
||||
/**
|
||||
* Minimize window
|
||||
*/
|
||||
handle('minimizeWindow', () => BrowserWindow.getFocusedWindow().minimize());
|
||||
|
||||
/**
|
||||
* Toggle fullscreen window
|
||||
*/
|
||||
handle('toggleFullscreenWindow', () => {
|
||||
const window = BrowserWindow.getFocusedWindow();
|
||||
window.isMaximized() ? window.unmaximize() : window.maximize();
|
||||
});
|
||||
|
||||
/**
|
||||
* Quit app
|
||||
*/
|
||||
handle('closeWindow', () => app.quit());
|
||||
@@ -1,5 +1,7 @@
|
||||
const { spawnAsyncJSONPromise } = require('../helpers/spawn-promise.js');
|
||||
const treeKill = require('tree-kill');
|
||||
const sanitizer = require('../utils/sanitizer.js');
|
||||
const { isWindows } = require('../helpers/platform.js');
|
||||
const { spawnAsyncJSONPromise } = require('../helpers/spawn-promise.js');
|
||||
|
||||
class Session {
|
||||
#id;
|
||||
@@ -63,7 +65,14 @@ class Session {
|
||||
if (this.isRunning) {
|
||||
this.#process.on('close', () => resolve());
|
||||
this.#process.on('error', (e) => reject(e));
|
||||
this.#process.kill();
|
||||
/**
|
||||
* On Windows OS, a spawned process uses cmd.exe to initiate a session.
|
||||
* Hence, captured process.pid corresponds to cmd.exe instead of session.
|
||||
* To avoid orphaned session processes and due to lack of node support
|
||||
* to handle killing processes cleanly in this scenario,
|
||||
* kill entire dependent process tree on Windows.
|
||||
*/
|
||||
isWindows() ? treeKill(this.#process.pid) : this.#process.kill();
|
||||
} else {
|
||||
// Do nothing when process isn't running
|
||||
resolve();
|
||||
|
||||
@@ -3590,6 +3590,11 @@ tough-cookie@~2.5.0:
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
|
||||
trim-newlines@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
|
||||
|
||||
@@ -19,14 +19,12 @@ import {
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import WindowMockIPC from '../helpers/window-mock-ipc';
|
||||
|
||||
module('Acceptance | authentication', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let mockIPC;
|
||||
let messageHandler;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -102,41 +100,11 @@ module('Acceptance | authentication', function (hooks) {
|
||||
urls.targets = `${urls.projects}/targets`;
|
||||
urls.sessions = `${urls.projects}/sessions`;
|
||||
|
||||
class MockIPC {
|
||||
origin = null;
|
||||
|
||||
invoke(method, payload) {
|
||||
return this[method](payload);
|
||||
}
|
||||
|
||||
getOrigin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
setOrigin(origin) {
|
||||
this.origin = origin;
|
||||
return this.origin;
|
||||
}
|
||||
}
|
||||
|
||||
mockIPC = new MockIPC();
|
||||
messageHandler = async function (event) {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
const { method, payload } = event.data;
|
||||
if (method) {
|
||||
const response = await mockIPC.invoke(method, payload);
|
||||
event.ports[0].postMessage(response);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
// Mock the postMessage interface used by IPC.
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultOrigin(this);
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
assert.expect(2);
|
||||
await visit(urls.index);
|
||||
|
||||
@@ -18,14 +18,12 @@ import {
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import WindowMockIPC from '../helpers/window-mock-ipc';
|
||||
|
||||
module('Acceptance | projects', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let mockIPC;
|
||||
let messageHandler;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -103,41 +101,11 @@ module('Acceptance | projects', function (hooks) {
|
||||
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
|
||||
urls.projects = `${urls.scopes.org}/projects`;
|
||||
|
||||
class MockIPC {
|
||||
origin = null;
|
||||
|
||||
invoke(method, payload) {
|
||||
return this[method](payload);
|
||||
}
|
||||
|
||||
getOrigin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
setOrigin(origin) {
|
||||
this.origin = origin;
|
||||
return this.origin;
|
||||
}
|
||||
}
|
||||
|
||||
mockIPC = new MockIPC();
|
||||
messageHandler = async function (event) {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
const { method, payload } = event.data;
|
||||
if (method) {
|
||||
const response = await mockIPC.invoke(method, payload);
|
||||
event.ports[0].postMessage(response);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
// Mock the postMessage interface used by IPC.
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultOrigin(this);
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
invalidateSession();
|
||||
assert.expect(2);
|
||||
|
||||
@@ -19,14 +19,12 @@ import {
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import WindowMockIPC from '../helpers/window-mock-ipc';
|
||||
|
||||
module('Acceptance | targets', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
|
||||
let mockIPC;
|
||||
let messageHandler;
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -106,41 +104,11 @@ module('Acceptance | targets', function (hooks) {
|
||||
urls.projects = `${urls.scopes.org}/projects`;
|
||||
urls.targets = `${urls.projects}/targets`;
|
||||
|
||||
class MockIPC {
|
||||
origin = null;
|
||||
|
||||
invoke(method, payload) {
|
||||
return this[method](payload);
|
||||
}
|
||||
|
||||
getOrigin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
setOrigin(origin) {
|
||||
this.origin = origin;
|
||||
return this.origin;
|
||||
}
|
||||
}
|
||||
|
||||
mockIPC = new MockIPC();
|
||||
messageHandler = async function (event) {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
const { method, payload } = event.data;
|
||||
if (method) {
|
||||
const response = await mockIPC.invoke(method, payload);
|
||||
event.ports[0].postMessage(response);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
// Mock the postMessage interface used by IPC.
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultOrigin(this);
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
invalidateSession();
|
||||
assert.expect(2);
|
||||
|
||||
@@ -17,6 +17,8 @@ class MockIPC {
|
||||
}
|
||||
|
||||
resetOrigin() {}
|
||||
isMacOS() {}
|
||||
isWindowsOS() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7643,7 +7643,7 @@ debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debuglog@*, debuglog@^1.0.1:
|
||||
debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
|
||||
@@ -12410,7 +12410,7 @@ import-lazy@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
|
||||
integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
|
||||
|
||||
imurmurhash@*, imurmurhash@^0.1.4:
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
@@ -14099,11 +14099,6 @@ lodash._baseflatten@^3.0.0:
|
||||
lodash.isarguments "^3.0.0"
|
||||
lodash.isarray "^3.0.0"
|
||||
|
||||
lodash._baseindexof@*:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
|
||||
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
|
||||
|
||||
lodash._baseuniq@~4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
|
||||
@@ -14112,16 +14107,11 @@ lodash._baseuniq@~4.6.0:
|
||||
lodash._createset "~4.0.0"
|
||||
lodash._root "~3.0.0"
|
||||
|
||||
lodash._bindcallback@*, lodash._bindcallback@^3.0.0:
|
||||
lodash._bindcallback@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
|
||||
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
|
||||
|
||||
lodash._cacheindexof@*:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
|
||||
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
|
||||
|
||||
lodash._createassigner@^3.0.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
|
||||
@@ -14131,19 +14121,12 @@ lodash._createassigner@^3.0.0:
|
||||
lodash._isiterateecall "^3.0.0"
|
||||
lodash.restparam "^3.0.0"
|
||||
|
||||
lodash._createcache@*:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
|
||||
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
|
||||
dependencies:
|
||||
lodash._getnative "^3.0.0"
|
||||
|
||||
lodash._createset@~4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
|
||||
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
|
||||
|
||||
lodash._getnative@*, lodash._getnative@^3.0.0:
|
||||
lodash._getnative@^3.0.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
|
||||
@@ -14361,7 +14344,7 @@ lodash.pick@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
|
||||
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
|
||||
|
||||
lodash.restparam@*, lodash.restparam@^3.0.0:
|
||||
lodash.restparam@^3.0.0:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
|
||||
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
|
||||
|
||||
Reference in new issue
Block a user