Remove usage of lodash methods (#21567)

* Create new local util lib to replace lodash.

* Replace usage of isInteger and debounce from lodash with highbar.

* Create isObject and runOnce utility methods.

* remove use of at, isObject, and once lodash method usage.

* remove map and transform lodash calls.

* Add memoize function to highbar.

* remove memoize lodash usage.

* remove merge and isEqual lodash methods and update other missing refs to highbar.

* convert the throttle to debounce.

* add throttle method to highbar.

* use the new throttle method instead of debounce where necessary.

* Add mergeDeep function for init config merge.

* remove lodash from the build process.

* Fix introduced bug in workspacesService.

* Added tests for highbar mergeDeep and expanded its functionality to support arrays.

* review updates.

* Added types to mergeDeep function.

* Add missing MapCache prototype methods.

* Add license notices, types and missing hash code.

* First pass at compare an array objects function.

* use new compareArrayObjs fn

* Add missing not

* Added types to arrayObjectIsEqual

* Add tests for arrayObjectIsEqual and fix some edge case bugs.

* update util fn name
This commit is contained in:
Jeff Pihach 2023-02-21 11:25:36 -06:00 committed by GitHub
parent 2f0c97d276
commit 5eafe86fa4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 733 additions and 118 deletions

View file

@ -57,7 +57,6 @@ module.exports = {
plugins: [ plugins: [
...plugins, ...plugins,
['babel-plugin-styled-components', { displayName: false, ssr: false }], ['babel-plugin-styled-components', { displayName: false, ssr: false }],
'babel-plugin-lodash',
], ],
}, },
}, },

View file

@ -45,7 +45,6 @@
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@types/jest": "^27.3.1", "@types/jest": "^27.3.1",
"@types/lodash": "4.14.149",
"@types/node": "^16.11.10", "@types/node": "^16.11.10",
"@types/react": "^16.8.19", "@types/react": "^16.8.19",
"@types/react-router-dom": "^4.3.3", "@types/react-router-dom": "^4.3.3",
@ -55,7 +54,6 @@
"@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/eslint-plugin": "^5.4.0",
"@typescript-eslint/parser": "^5.4.0", "@typescript-eslint/parser": "^5.4.0",
"babel-loader": "^8.2.5", "babel-loader": "^8.2.5",
"babel-plugin-lodash": "^3.3.4",
"clean-webpack-plugin": "4.0.0", "clean-webpack-plugin": "4.0.0",
"core-js": "^3", "core-js": "^3",
"cross-env": "5.0.5", "cross-env": "5.0.5",
@ -76,7 +74,6 @@
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"jest": "^27.3.1", "jest": "^27.3.1",
"jest-styled-components": "^7.0.8", "jest-styled-components": "^7.0.8",
"lodash-webpack-plugin": "^0.11.6",
"msw": "^0.47.4", "msw": "^0.47.4",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"prettier": "^2.5.0", "prettier": "^2.5.0",

View file

@ -18,7 +18,6 @@ const path = require('path');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const HtmlWebPackPlugin = require('html-webpack-plugin'); const HtmlWebPackPlugin = require('html-webpack-plugin');
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
@ -51,9 +50,6 @@ const configFactory = {
...options, ...options,
}); });
}, },
lodash() {
return new LodashModuleReplacementPlugin();
},
bundleAnalyzer(options) { bundleAnalyzer(options) {
return new BundleAnalyzerPlugin({ analyzerHost: '0.0.0.0', ...options }); return new BundleAnalyzerPlugin({ analyzerHost: '0.0.0.0', ...options });
}, },

View file

@ -19,7 +19,7 @@ const configFactory = require('./webpack.base');
process.env.BABEL_ENV = 'production'; process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production'; process.env.NODE_ENV = 'production';
const plugins = [configFactory.plugins.lodash()]; const plugins = [];
if (process.env.WEBPACK_ANALYZE_BUNDLE === 'true') { if (process.env.WEBPACK_ANALYZE_BUNDLE === 'true') {
plugins.push(configFactory.plugins.bundleAnalyzer()); plugins.push(configFactory.plugins.bundleAnalyzer());

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { isObject } from 'lodash'; import { isObject } from 'shared/utils/highbar';
import Logger from '../../libs/logger'; import Logger from '../../libs/logger';

View file

@ -13,7 +13,6 @@
"ace-builds": "1.4.6", "ace-builds": "1.4.6",
"create-react-class": "^15.6.3", "create-react-class": "^15.6.3",
"cross-env": "5.0.5", "cross-env": "5.0.5",
"lodash": "^4.17.21",
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"react": "^16.8.4", "react": "^16.8.4",
"react-day-picker": "7.3.2", "react-day-picker": "7.3.2",

View file

@ -0,0 +1,221 @@
/**
* Copyright 2023 Gravitational, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { arrayObjectIsEqual, mergeDeep } from './highbar';
describe('mergeDeep can merge two', () => {
it('objects together', () => {
const a = { a: 1, b: 2, c: 3, e: 5 };
const b = { a: 3, b: 2, c: 1, d: 4 };
expect(mergeDeep(a, b)).toStrictEqual({
a: 3,
b: 2,
c: 1,
d: 4,
e: 5,
});
});
it('nested objects together', () => {
const a = { a: 1, b: 2, c: { d: 3, e: 6, g: 8 } };
const b = { a: 1, b: 2, c: { d: 4, e: 6, f: 7 } };
expect(mergeDeep(a, b)).toStrictEqual({
a: 1,
b: 2,
c: { d: 4, e: 6, f: 7, g: 8 },
});
});
it('objects together that contain arrays', () => {
const a = { a: 1, b: ['a', 'b', 'd'] };
const b = { a: 2, b: ['b', 'c'] };
expect(mergeDeep(a, b)).toStrictEqual({
a: 2,
b: ['b', 'c', 'd'],
});
const c = { a: 1, b: ['b', 'c'] };
const d = { a: 2, b: ['a', 'b', 'd'] };
expect(mergeDeep(c, d)).toStrictEqual({
a: 2,
b: ['a', 'b', 'd'],
});
});
it('objects together that contain arrays of arrays', () => {
const a = { a: [['b', 'c', 'f']] };
const b = { a: [['d', 'e']] };
expect(mergeDeep(a, b)).toStrictEqual({
a: [['d', 'e', 'f']],
});
const c = { a: [['d', 'e']] };
const d = { a: [['b', 'c', 'f']] };
expect(mergeDeep(c, d)).toStrictEqual({
a: [['b', 'c', 'f']],
});
});
it('objects together that contain arrays that contain objects', () => {
const a = { a: 1, b: [{ c: 3, d: 4, e: 5 }, 'b'] };
const b = { a: 2, b: [{ c: 3, d: 4, f: 6 }, 'c'] };
expect(mergeDeep(a, b)).toStrictEqual({
a: 2,
b: [{ c: 3, d: 4, e: 5, f: 6 }, 'c'],
});
});
it('objects with arrays with undefined indexes', () => {
const a = {
a: false,
b: {
c: 'foo',
d: 'bar',
},
c: {
a: 'no',
b: [],
},
};
const b = {
a: true,
b: {
d: 'baz',
e: 'bax',
},
c: {
a: 'ok',
b: [
{
a: 'foo',
b: 'bar',
},
],
},
};
expect(mergeDeep(a, b)).toStrictEqual({
a: true,
b: {
c: 'foo',
d: 'baz',
e: 'bax',
},
c: {
a: 'ok',
b: [
{
a: 'foo',
b: 'bar',
},
],
},
});
});
});
describe('arrayObjectIsEqual correctly compares', () => {
it('simple arrays', () => {
const a = [{ foo: 'bar' }];
const b = [{ foo: 'bar' }];
expect(arrayObjectIsEqual(a, b)).toBe(true);
const c = [{ foo: 'bar' }];
const d = [{ foo: 'baz' }];
expect(arrayObjectIsEqual(c, d)).toBe(false);
});
it('arrays with complex objects', () => {
const a = [
{
'/clusters/test-uri': {
accessRequests: {
pending: {
app: {},
db: {},
kube_cluster: {},
node: {},
role: {},
windows_desktop: {},
},
isBarCollapsed: false,
},
localClusterUri: '/clusters/test-uri',
documents: [
{
kind: 'doc.cluster',
title: 'Cluster Test',
clusterUri: '/clusters/test-uri',
uri: '/docs/test-cluster-uri',
},
],
location: '/docs/test-cluster-uri',
previous: {
documents: [
{
kind: 'doc.terminal_shell',
uri: '/docs/some_uri',
title: '/Users/alice/Documents',
},
],
location: '/docs/some_uri',
},
},
},
];
const b = [
{
'/clusters/test-uri': {
accessRequests: {
pending: {
app: {},
db: {},
kube_cluster: {},
node: {},
role: {},
windows_desktop: {},
},
isBarCollapsed: false,
},
localClusterUri: '/clusters/test-uri',
documents: [
{
kind: 'doc.cluster',
title: 'Cluster Test',
clusterUri: '/clusters/test-uri',
uri: '/docs/test-cluster-uri',
},
],
location: '/docs/test-cluster-uri',
previous: {
documents: [
{
kind: 'doc.terminal_shell',
uri: '/docs/some_uri',
title: '/Users/alice/Documents',
},
],
location: '/docs/some_uri',
},
},
},
];
expect(arrayObjectIsEqual(a, b)).toBe(true);
});
});

View file

@ -0,0 +1,459 @@
/**
* Copyright 2023 Gravitational, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type SupportedMergeTypes = string | number | Record<string, unknown>;
type MergeTarget = Record<string, unknown> | Array<SupportedMergeTypes>;
export function mergeDeep(target: MergeTarget, ...sources: Array<MergeTarget>) {
const isObject = obj => obj && typeof obj === 'object' && !Array.isArray(obj);
const mergeArray = (target, source) => {
source.forEach((value, index) => {
if (
Array.isArray(value) ||
(isObject(value) && isObject(source[index]))
) {
if (target[index] === undefined) {
target[index] = source[index];
} else {
mergeDeep(target[index], source[index]);
}
} else {
target[index] = source[index];
}
});
};
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else if (Array.isArray(source[key])) {
mergeArray(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
} else if (Array.isArray(target) && Array.isArray(source)) {
mergeArray(target, source);
}
return mergeDeep(target, ...sources);
}
type CompareArray = Array<Record<string, unknown>>;
export function arrayObjectIsEqual(
arr1: CompareArray,
arr2: CompareArray
): boolean {
const compareArrays = (arr1, arr2) =>
arr1.length === arr2.length &&
arr1.every((obj, idx) => compareObjects(obj, arr2[idx]));
const compareObjects = (obj1, obj2) => {
if (!isObject(obj1)) {
return obj1 === obj2;
}
if (Object.keys(obj1).length) {
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;
}
return Object.keys(obj1).every(key => {
return compareObjects(obj1[key], obj2[key]);
});
} else {
return true;
}
};
return compareArrays(arr1, arr2);
}
export function isInteger(checkVal: any): boolean {
return Number.isInteger(checkVal) || checkVal == parseInt(checkVal);
}
export function isObject(checkVal: unknown): boolean {
const type = typeof checkVal;
return checkVal != null && (type == 'object' || type == 'function');
}
/**
* Lodash <https://lodash.com/>
* Copyright JS Foundation and other contributors <https://js.foundation/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
export function runOnce<T extends (...args) => any>(func: T) {
let n = 2;
let result;
return function () {
if (--n > 0) {
result = func.apply(this, arguments);
}
if (n <= 1) {
func = undefined;
}
return result;
};
}
interface ThrottleSettings {
leading?: boolean | undefined;
trailing?: boolean | undefined;
}
/**
* Lodash <https://lodash.com/>
* Copyright JS Foundation and other contributors <https://js.foundation/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
export function throttle<T extends (...args: any) => any>(
func: T,
wait = 0,
options?: ThrottleSettings
): DebouncedFunc<T> {
var leading = true,
trailing = true;
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
return debounce(func, wait, {
leading: leading,
maxWait: wait,
trailing: trailing,
});
}
export type DebouncedFunc<T extends (...args: any[]) => any> = {
(...args: Parameters<T>): ReturnType<T> | undefined;
cancel(): void;
flush(): ReturnType<T> | undefined;
};
type DebounceSettings = {
leading?: boolean | undefined;
maxWait?: number | undefined;
trailing?: boolean | undefined;
};
/**
* Lodash <https://lodash.com/>
* Copyright JS Foundation and other contributors <https://js.foundation/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
export function debounce<T extends (...args: any) => any>(
func: T,
wait = 0,
options?: DebounceSettings
): DebouncedFunc<T> {
var lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime,
lastInvokeTime = 0,
leading = false,
maxing = false,
trailing = true;
if (isObject(options)) {
leading = !!options.leading;
maxing = 'maxWait' in options;
maxWait = maxing ? Math.max(options.maxWait || 0, wait) : maxWait;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
function invokeFunc(time) {
var args = lastArgs,
thisArg = lastThis;
lastArgs = lastThis = undefined;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time;
// Start the timer for the trailing edge.
timerId = setTimeout(timerExpired, wait);
// Invoke the leading edge.
return leading ? invokeFunc(time) : result;
}
function remainingWait(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime,
timeWaiting = wait - timeSinceLastCall;
return maxing
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting;
}
function shouldInvoke(time) {
var timeSinceLastCall = time - lastCallTime,
timeSinceLastInvoke = time - lastInvokeTime;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return (
lastCallTime === undefined ||
timeSinceLastCall >= wait ||
timeSinceLastCall < 0 ||
(maxing && timeSinceLastInvoke >= maxWait)
);
}
function timerExpired() {
var time = Date.now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
// Restart the timer.
timerId = setTimeout(timerExpired, remainingWait(time));
}
function trailingEdge(time) {
timerId = undefined;
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = undefined;
return result;
}
function cancel() {
if (timerId !== undefined) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = undefined;
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now());
}
function debounced() {
var time = Date.now(),
isInvoking = shouldInvoke(time);
lastArgs = arguments;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime);
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (timerId === undefined) {
timerId = setTimeout(timerExpired, wait);
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}
interface MapCacheType {
delete(key: any): boolean;
get(key: any): any;
has(key: any): boolean;
set(key: any, value: any): this;
clear?: (() => void) | undefined;
}
type MemoizedFunction = {
cache: MapCacheType;
};
/**
* Lodash <https://lodash.com/>
* Copyright JS Foundation and other contributors <https://js.foundation/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
export function memoize<T extends (...args: any) => any>(
func: T
): T & MemoizedFunction {
const memoized = function () {
const args = arguments;
const key = args[0];
const cache = memoized.cache;
if (cache.has(key)) {
return cache.get(key);
}
const result = func.apply(this, args);
memoized.cache = cache.set(key, result) || cache;
return result;
};
memoized.cache = new (memoize.Cache || MapCache)();
/* eslint-disable @typescript-eslint/ban-ts-comment*/
// @ts-ignore
return memoized;
}
// Expose `MapCache`.
memoize.Cache = MapCache;
function MapCache(entries?: any) {
let index = -1;
const length = entries == null ? 0 : entries.length;
this.clear();
while (++index < length) {
const entry = entries[index];
this.set(entry[0], entry[1]);
}
}
function mapCacheClear() {
this.size = 0;
this.__data__ = {
hash: new Hash(),
map: new Map(),
string: new Hash(),
};
}
function mapCacheDelete(key) {
var result = getMapData(this, key)['delete'](key);
this.size -= result ? 1 : 0;
return result;
}
function mapCacheGet(key) {
return getMapData(this, key).get(key);
}
function mapCacheHas(key) {
return getMapData(this, key).has(key);
}
function mapCacheSet(key, value) {
var data = getMapData(this, key),
size = data.size;
data.set(key, value);
this.size += data.size == size ? 0 : 1;
return this;
}
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;
function Hash(entries?) {
var index = -1,
length = entries == null ? 0 : entries.length;
this.clear();
while (++index < length) {
var entry = entries[index];
this.set(entry[0], entry[1]);
}
}
const HASH_UNDEFINED = '__lodash_hash_undefined__';
function hashClear() {
this.__data__ = Object.create ? Object.create(null) : {};
this.size = 0;
}
function hashDelete(key) {
var result = this.has(key) && delete this.__data__[key];
this.size -= result ? 1 : 0;
return result;
}
function hashGet(key) {
var data = this.__data__;
if (Object.create) {
var result = data[key];
return result === HASH_UNDEFINED ? undefined : result;
}
return Object.hasOwnProperty.call(data, key) ? data[key] : undefined;
}
function hashHas(key) {
var data = this.__data__;
return Object.create
? data[key] !== undefined
: Object.hasOwnProperty.call(data, key);
}
function hashSet(key, value) {
var data = this.__data__;
this.size += this.has(key) ? 0 : 1;
data[key] = Object.create && value === undefined ? HASH_UNDEFINED : value;
return this;
}
// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;
function getMapData(map, key) {
var data = map.__data__;
return isKeyable(key)
? data[typeof key == 'string' ? 'string' : 'hash']
: data.map;
}
function isKeyable(value) {
var type = typeof value;
return type == 'string' ||
type == 'number' ||
type == 'symbol' ||
type == 'boolean'
? value !== '__proto__'
: value === null;
}

View file

@ -19,7 +19,7 @@ import styled from 'styled-components';
import Popover from 'design/Popover'; import Popover from 'design/Popover';
import theme from 'design/theme'; import theme from 'design/theme';
import { Box } from 'design'; import { Box } from 'design';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
export default function JoinedUsers(props) { export default function JoinedUsers(props) {
const { active, users, open = false, ml, mr } = props; const { active, users, open = false, ml, mr } = props;

View file

@ -16,7 +16,7 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { throttle } from 'lodash'; import { throttle } from 'shared/utils/highbar';
import { dateToUtc } from 'shared/services/loc'; import { dateToUtc } from 'shared/services/loc';
import { format } from 'date-fns'; import { format } from 'date-fns';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import { throttle } from 'lodash'; import { throttle } from 'shared/utils/highbar';
import TtyPlayer from 'teleport/lib/term/ttyPlayer'; import TtyPlayer from 'teleport/lib/term/ttyPlayer';

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import { throttle } from 'lodash'; import { throttle } from 'shared/utils/highbar';
export default function useTtyBpfMapper(tty, events) { export default function useTtyBpfMapper(tty, events) {
// create a map [time][index] for quick lookups // create a map [time][index] for quick lookups

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import { throttle } from 'lodash'; import { throttle } from 'shared/utils/highbar';
import Logger from 'shared/libs/logger'; import Logger from 'shared/libs/logger';
import session from 'teleport/services/websession'; import session from 'teleport/services/websession';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import styled from 'styled-components'; import styled from 'styled-components';
import { height, space, color } from 'design/system'; import { height, space, color } from 'design/system';

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import { generatePath } from 'react-router'; import { generatePath } from 'react-router';
import { merge } from 'lodash'; import { mergeDeep } from 'shared/utils/highbar';
import generateResourcePath from './generateResourcePath'; import generateResourcePath from './generateResourcePath';
@ -554,7 +554,7 @@ const cfg = {
}, },
init(backendConfig = {}) { init(backendConfig = {}) {
merge(this, backendConfig); mergeDeep(this, backendConfig);
}, },
}; };

View file

@ -16,12 +16,14 @@ limitations under the License.
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
import { Terminal } from 'xterm'; import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit'; import { FitAddon } from 'xterm-addon-fit';
import { debounce, Cancelable, isInteger } from 'lodash'; import { debounce, isInteger } from 'shared/utils/highbar';
import Logger from 'shared/libs/logger'; import Logger from 'shared/libs/logger';
import { TermEvent } from './enums'; import { TermEvent } from './enums';
import Tty from './tty'; import Tty from './tty';
import type { DebouncedFunc } from 'shared/utils/highbar';
const logger = Logger.create('lib/term/terminal'); const logger = Logger.create('lib/term/terminal');
const DISCONNECT_TXT = 'disconnected'; const DISCONNECT_TXT = 'disconnected';
const WINDOW_RESIZE_DEBOUNCE_DELAY = 200; const WINDOW_RESIZE_DEBOUNCE_DELAY = 200;
@ -37,7 +39,7 @@ export default class TtyTerminal {
_scrollBack: number; _scrollBack: number;
_fontFamily: string; _fontFamily: string;
_fontSize: number; _fontSize: number;
_debouncedResize: (() => void) & Cancelable; _debouncedResize: DebouncedFunc<() => void>;
_fitAddon = new FitAddon(); _fitAddon = new FitAddon();
constructor(tty: Tty, options: Options) { constructor(tty: Tty, options: Options) {

View file

@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { map } from 'lodash';
import api from 'teleport/services/api'; import api from 'teleport/services/api';
import cfg from 'teleport/config'; import cfg from 'teleport/config';
@ -26,7 +24,7 @@ const service = {
fetchSessions(clusterId) { fetchSessions(clusterId) {
return api.get(cfg.getTerminalSessionUrl({ clusterId })).then(response => { return api.get(cfg.getTerminalSessionUrl({ clusterId })).then(response => {
if (response && response.sessions) { if (response && response.sessions) {
return map(response.sessions, makeSession); return response.sessions.map(makeSession);
} }
return []; return [];
@ -44,7 +42,7 @@ const service = {
const parties: ParticipantList = {}; const parties: ParticipantList = {};
json.sessions.forEach(s => { json.sessions.forEach(s => {
parties[s.id] = map(s.parties, makeParticipant); parties[s.id] = s.parties.map(makeParticipant);
}); });
return parties; return parties;

View file

@ -14,12 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
import { at } from 'lodash';
import { ResetToken } from './types'; import { ResetToken } from './types';
export default function makeResetToken(json): ResetToken { export default function makeResetToken(json): ResetToken {
const [expires, username, value] = at(json, ['expiry', 'user', 'tokenId']); const { expires, username, value } = json;
return { return {
username, username,
expires: new Date(expires), expires: new Date(expires),

View file

@ -16,7 +16,7 @@
import fs, { existsSync, readFileSync, writeFileSync } from 'fs'; import fs, { existsSync, readFileSync, writeFileSync } from 'fs';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import Logger from 'teleterm/logger'; import Logger from 'teleterm/logger';

View file

@ -19,7 +19,7 @@ import winston, {
format, format,
transports, transports,
} from 'winston'; } from 'winston';
import { isObject } from 'lodash'; import { isObject } from 'shared/utils/highbar';
import split2 from 'split2'; import split2 from 'split2';

View file

@ -42,7 +42,7 @@
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { memoize } from 'lodash'; import { memoize } from 'shared/utils/highbar';
import Logger from 'teleterm/logger'; import Logger from 'teleterm/logger';
import { unique } from 'teleterm/ui/utils/uid'; import { unique } from 'teleterm/ui/utils/uid';

View file

@ -15,7 +15,7 @@
*/ */
import * as grpc from '@grpc/grpc-js'; import * as grpc from '@grpc/grpc-js';
import { isObject, transform } from 'lodash'; import { isObject } from 'shared/utils/highbar';
import Logger from 'teleterm/logger'; import Logger from 'teleterm/logger';
@ -127,24 +127,24 @@ export const withLogging = (logger: Logger): UnaryInterceptor => {
}; };
function filterSensitiveProperties(toFilter: object): object { function filterSensitiveProperties(toFilter: object): object {
return transform( const acc = {};
toFilter, const transformer = (result: object, value: any, key: any) => {
(result: object, value: any, key: any) => { if (
if ( SENSITIVE_PROPERTIES.some(
SENSITIVE_PROPERTIES.some( sensitiveProp => typeof key === 'string' && key.includes(sensitiveProp)
sensitiveProp => )
typeof key === 'string' && key.includes(sensitiveProp) ) {
) result[key] = '~FILTERED~';
) { return;
result[key] = '~FILTERED~'; }
return; if (isObject(value)) {
} result[key] = filterSensitiveProperties(value);
if (isObject(value)) { return;
result[key] = filterSensitiveProperties(value); }
return; result[key] = value;
} };
result[key] = value;
}, Object.keys(toFilter).forEach(key => transformer(acc, toFilter[key], key));
{}
); return acc;
} }

View file

@ -16,7 +16,7 @@
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import { space, width, color, height } from 'styled-system'; import { space, width, color, height } from 'styled-system';
export default function ClusterSearch(props: Props) { export default function ClusterSearch(props: Props) {

View file

@ -15,7 +15,7 @@
*/ */
import React, { useMemo, useRef } from 'react'; import React, { useMemo, useRef } from 'react';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import { Box, ButtonSecondary, Flex, Link, Text } from 'design'; import { Box, ButtonSecondary, Flex, Link, Text } from 'design';
import Validation from 'shared/components/Validation'; import Validation from 'shared/components/Validation';
import * as Alerts from 'design/Alert'; import * as Alerts from 'design/Alert';

View file

@ -14,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { debounce } from 'lodash';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import styled, { useTheme } from 'styled-components'; import styled, { useTheme } from 'styled-components';
import { Box, Flex } from 'design'; import { Box, Flex } from 'design';
import { debounce } from 'shared/utils/highbar';
import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost';

View file

@ -17,7 +17,7 @@ limitations under the License.
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
import { IDisposable, Terminal } from 'xterm'; import { IDisposable, Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit'; import { FitAddon } from 'xterm-addon-fit';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost'; import { IPtyProcess } from 'teleterm/sharedProcess/ptyHost';
import Logger from 'teleterm/logger'; import Logger from 'teleterm/logger';

View file

@ -18,7 +18,7 @@ import { useEffect } from 'react';
import { useAsync } from 'shared/hooks/useAsync'; import { useAsync } from 'shared/hooks/useAsync';
import { once } from 'lodash'; import { runOnce } from 'shared/utils/highbar';
import { useAppContext } from 'teleterm/ui/appContextProvider'; import { useAppContext } from 'teleterm/ui/appContextProvider';
import { IAppContext } from 'teleterm/ui/types'; import { IAppContext } from 'teleterm/ui/types';
@ -131,7 +131,7 @@ async function initState(
removeInitCommand(); removeInitCommand();
}); });
const markDocumentAsConnectedOnce = once(() => { const markDocumentAsConnectedOnce = runOnce(() => {
docsService.update(doc.uri, { status: 'connected' }); docsService.update(doc.uri, { status: 'connected' });
}); });

View file

@ -16,7 +16,7 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import { Box, Flex } from 'design'; import { Box, Flex } from 'design';
import { color, height, space, width } from 'styled-system'; import { color, height, space, width } from 'styled-system';
import { Spinner } from 'design/Icon'; import { Spinner } from 'design/Icon';

View file

@ -15,7 +15,7 @@
*/ */
import React, { Fragment, useMemo, useState } from 'react'; import React, { Fragment, useMemo, useState } from 'react';
import { debounce } from 'lodash'; import { debounce } from 'shared/utils/highbar';
import styled from 'styled-components'; import styled from 'styled-components';
import { prepareVirtualScrollItems } from './prepareVirtualScrollItems'; import { prepareVirtualScrollItems } from './prepareVirtualScrollItems';

View file

@ -85,7 +85,7 @@ describe('restoring workspace', () => {
return { workspacesService, clusterDocument }; return { workspacesService, clusterDocument };
} }
it('restores the workspace if it there is a persisted state for given clusterUri', () => { it('restores the workspace if there is a persisted state for given clusterUri', () => {
const testClusterUri = '/clusters/test-uri'; const testClusterUri = '/clusters/test-uri';
const testWorkspace: Workspace = { const testWorkspace: Workspace = {
accessRequests: { accessRequests: {

View file

@ -15,8 +15,8 @@
*/ */
import { useStore } from 'shared/libs/stores'; import { useStore } from 'shared/libs/stores';
import { arrayObjectIsEqual } from 'shared/utils/highbar';
import { isEqual } from 'lodash';
/* eslint-disable @typescript-eslint/ban-ts-comment*/ /* eslint-disable @typescript-eslint/ban-ts-comment*/
// @ts-ignore // @ts-ignore
import { ResourceKind } from 'e-teleport/Workflow/NewRequest/useNewRequest'; import { ResourceKind } from 'e-teleport/Workflow/NewRequest/useNewRequest';
@ -344,7 +344,7 @@ export class WorkspacesService extends ImmutableStore<WorkspacesState> {
return ( return (
previousDocuments?.length && previousDocuments?.length &&
!isEqual( !arrayObjectIsEqual(
omitUriAndTitle(previousDocuments), omitUriAndTitle(previousDocuments),
omitUriAndTitle(currentDocuments) omitUriAndTitle(currentDocuments)
) )

View file

@ -202,13 +202,6 @@
dependencies: dependencies:
"@babel/types" "^7.16.0" "@babel/types" "^7.16.0"
"@babel/helper-module-imports@^7.0.0-beta.49":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
dependencies:
"@babel/types" "^7.18.6"
"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.0": "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.0":
version "7.16.0" version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5"
@ -280,11 +273,6 @@
dependencies: dependencies:
"@babel/types" "^7.16.0" "@babel/types" "^7.16.0"
"@babel/helper-string-parser@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
"@babel/helper-validator-identifier@^7.15.7": "@babel/helper-validator-identifier@^7.15.7":
version "7.15.7" version "7.15.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
@ -295,11 +283,6 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
"@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/helper-validator-option@^7.14.5": "@babel/helper-validator-option@^7.14.5":
version "7.14.5" version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
@ -1155,15 +1138,6 @@
"@babel/helper-validator-identifier" "^7.15.7" "@babel/helper-validator-identifier" "^7.15.7"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.6":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7"
integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==
dependencies:
"@babel/helper-string-parser" "^7.19.4"
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3": "@bcoe/v8-coverage@^0.2.3":
version "0.2.3" version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@ -3118,11 +3092,6 @@
dependencies: dependencies:
keyv "*" keyv "*"
"@types/lodash@4.14.149":
version "4.14.149"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440"
integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==
"@types/long@^4.0.1": "@types/long@^4.0.1":
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
@ -4532,17 +4501,6 @@ babel-plugin-jest-hoist@^27.4.0:
"@types/babel__core" "^7.0.0" "@types/babel__core" "^7.0.0"
"@types/babel__traverse" "^7.0.6" "@types/babel__traverse" "^7.0.6"
babel-plugin-lodash@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196"
integrity sha512-yDZLjK7TCkWl1gpBeBGmuaDIFhZKmkoL+Cu2MUUjv5VxUZx/z7tBGBCBcQs5RI1Bkz5LLmNdjx7paOyQtMovyg==
dependencies:
"@babel/helper-module-imports" "^7.0.0-beta.49"
"@babel/types" "^7.0.0-beta.49"
glob "^7.1.1"
lodash "^4.17.10"
require-package-name "^2.0.1"
babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0:
version "2.8.0" version "2.8.0"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
@ -10424,13 +10382,6 @@ locate-path@^6.0.0:
dependencies: dependencies:
p-locate "^5.0.0" p-locate "^5.0.0"
lodash-webpack-plugin@^0.11.6:
version "0.11.6"
resolved "https://registry.yarnpkg.com/lodash-webpack-plugin/-/lodash-webpack-plugin-0.11.6.tgz#8204c6b78beb62ce5211217dfe783c21557ecd33"
integrity sha512-nsHN/+IxZK/C425vGC8pAxkKJ8KQH2+NJnhDul14zYNWr6HJcA95w+oRR7Cp0oZpOdMplDZXmjVROp8prPk7ig==
dependencies:
lodash "^4.17.20"
lodash.camelcase@^4.3.0: lodash.camelcase@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@ -10451,7 +10402,7 @@ lodash.uniq@4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@ -13113,11 +13064,6 @@ require-in-the-middle@^5.0.3:
module-details-from-path "^1.0.3" module-details-from-path "^1.0.3"
resolve "^1.22.1" resolve "^1.22.1"
require-package-name@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9"
integrity sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==
requireindex@^1.2.0: requireindex@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef"