[MM-60609][MM-60612] Include Desktop App metrics in PerformanceReporter, add metrics in Prometheus for CPU/Memory usage (#28825)
* [MM-60609][MM-60612] Include Desktop App metrics in PerformanceReporter, add metrics in Prometheus for CPU/Memory usage * Fix mocks * PR feedback
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
2d96053012
Коммит
0c90b0363b
@@ -72,6 +72,10 @@ func (a *App) RegisterPerformanceReport(rctx request.CTX, report *model.Performa
|
|||||||
a.Metrics().ObserveMobileClientChannelSwitchDuration(commonLabels["platform"], h.Value/1000)
|
a.Metrics().ObserveMobileClientChannelSwitchDuration(commonLabels["platform"], h.Value/1000)
|
||||||
case model.MobileClientTeamSwitchDuration:
|
case model.MobileClientTeamSwitchDuration:
|
||||||
a.Metrics().ObserveMobileClientTeamSwitchDuration(commonLabels["platform"], h.Value/1000)
|
a.Metrics().ObserveMobileClientTeamSwitchDuration(commonLabels["platform"], h.Value/1000)
|
||||||
|
case model.DesktopClientCPUUsage:
|
||||||
|
a.Metrics().ObserveDesktopCpuUsage(commonLabels["platform"], commonLabels["desktop_app_version"], h.Labels["process"], h.Value)
|
||||||
|
case model.DesktopClientMemoryUsage:
|
||||||
|
a.Metrics().ObserveDesktopMemoryUsage(commonLabels["platform"], commonLabels["desktop_app_version"], h.Labels["process"], h.Value/1000)
|
||||||
default:
|
default:
|
||||||
// we intentionally skip unknown metrics
|
// we intentionally skip unknown metrics
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,4 +120,6 @@ type MetricsInterface interface {
|
|||||||
ObserveMobileClientTeamSwitchDuration(platform string, elapsed float64)
|
ObserveMobileClientTeamSwitchDuration(platform string, elapsed float64)
|
||||||
ClearMobileClientSessionMetadata()
|
ClearMobileClientSessionMetadata()
|
||||||
ObserveMobileClientSessionMetadata(version string, platform string, value float64, notificationDisabled string)
|
ObserveMobileClientSessionMetadata(version string, platform string, value float64, notificationDisabled string)
|
||||||
|
ObserveDesktopCpuUsage(platform, version, process string, usage float64)
|
||||||
|
ObserveDesktopMemoryUsage(platform, version, process string, usage float64)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,6 +353,16 @@ func (_m *MetricsInterface) ObserveClusterRequestDuration(elapsed float64) {
|
|||||||
_m.Called(elapsed)
|
_m.Called(elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ObserveDesktopCpuUsage provides a mock function with given fields: platform, version, process, usage
|
||||||
|
func (_m *MetricsInterface) ObserveDesktopCpuUsage(platform string, version string, process string, usage float64) {
|
||||||
|
_m.Called(platform, version, process, usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObserveDesktopMemoryUsage provides a mock function with given fields: platform, version, process, usage
|
||||||
|
func (_m *MetricsInterface) ObserveDesktopMemoryUsage(platform string, version string, process string, usage float64) {
|
||||||
|
_m.Called(platform, version, process, usage)
|
||||||
|
}
|
||||||
|
|
||||||
// ObserveEnabledUsers provides a mock function with given fields: users
|
// ObserveEnabledUsers provides a mock function with given fields: users
|
||||||
func (_m *MetricsInterface) ObserveEnabledUsers(users int64) {
|
func (_m *MetricsInterface) ObserveEnabledUsers(users int64) {
|
||||||
_m.Called(users)
|
_m.Called(users)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const (
|
|||||||
MetricsSubsystemNotifications = "notifications"
|
MetricsSubsystemNotifications = "notifications"
|
||||||
MetricsSubsystemClientsMobileApp = "mobileapp"
|
MetricsSubsystemClientsMobileApp = "mobileapp"
|
||||||
MetricsSubsystemClientsWeb = "webapp"
|
MetricsSubsystemClientsWeb = "webapp"
|
||||||
|
MetricsSubsystemClientsDesktopApp = "desktopapp"
|
||||||
MetricsCloudInstallationLabel = "installationId"
|
MetricsCloudInstallationLabel = "installationId"
|
||||||
MetricsCloudDatabaseClusterLabel = "databaseClusterName"
|
MetricsCloudDatabaseClusterLabel = "databaseClusterName"
|
||||||
MetricsCloudInstallationGroupLabel = "installationGroupId"
|
MetricsCloudInstallationGroupLabel = "installationGroupId"
|
||||||
@@ -216,6 +217,9 @@ type MetricsInterfaceImpl struct {
|
|||||||
MobileClientChannelSwitchDuration *prometheus.HistogramVec
|
MobileClientChannelSwitchDuration *prometheus.HistogramVec
|
||||||
MobileClientTeamSwitchDuration *prometheus.HistogramVec
|
MobileClientTeamSwitchDuration *prometheus.HistogramVec
|
||||||
MobileClientSessionMetadataGauge *prometheus.GaugeVec
|
MobileClientSessionMetadataGauge *prometheus.GaugeVec
|
||||||
|
|
||||||
|
DesktopClientCPUUsage *prometheus.HistogramVec
|
||||||
|
DesktopClientMemoryUsage *prometheus.HistogramVec
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -1347,6 +1351,30 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf
|
|||||||
)
|
)
|
||||||
m.Registry.MustRegister(m.MobileClientSessionMetadataGauge)
|
m.Registry.MustRegister(m.MobileClientSessionMetadataGauge)
|
||||||
|
|
||||||
|
m.DesktopClientCPUUsage = prometheus.NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Namespace: MetricsNamespace,
|
||||||
|
Subsystem: MetricsSubsystemClientsDesktopApp,
|
||||||
|
Name: "cpu_usage",
|
||||||
|
Help: "Average CPU usage of a specific process over an interval",
|
||||||
|
Buckets: []float64{0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 80, 100},
|
||||||
|
},
|
||||||
|
[]string{"platform", "version", "processName"},
|
||||||
|
)
|
||||||
|
m.Registry.MustRegister(m.DesktopClientCPUUsage)
|
||||||
|
|
||||||
|
m.DesktopClientMemoryUsage = prometheus.NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Namespace: MetricsNamespace,
|
||||||
|
Subsystem: MetricsSubsystemClientsDesktopApp,
|
||||||
|
Name: "memory_usage",
|
||||||
|
Help: "Memory usage in MB of a specific process",
|
||||||
|
Buckets: []float64{0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 3000, 5000},
|
||||||
|
},
|
||||||
|
[]string{"platform", "version", "processName"},
|
||||||
|
)
|
||||||
|
m.Registry.MustRegister(m.DesktopClientMemoryUsage)
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1850,6 +1878,14 @@ func (mi *MetricsInterfaceImpl) ObserveGlobalThreadsLoadDuration(platform, agent
|
|||||||
mi.ClientGlobalThreadsLoadDuration.With(prometheus.Labels{"platform": platform, "agent": agent}).Observe(elapsed)
|
mi.ClientGlobalThreadsLoadDuration.With(prometheus.Labels{"platform": platform, "agent": agent}).Observe(elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mi *MetricsInterfaceImpl) ObserveDesktopCpuUsage(platform, version, process string, usage float64) {
|
||||||
|
mi.DesktopClientCPUUsage.With(prometheus.Labels{"platform": platform, "version": version, "processName": process}).Observe(usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mi *MetricsInterfaceImpl) ObserveDesktopMemoryUsage(platform, version, process string, usage float64) {
|
||||||
|
mi.DesktopClientMemoryUsage.With(prometheus.Labels{"platform": platform, "version": version, "processName": process}).Observe(usage)
|
||||||
|
}
|
||||||
|
|
||||||
func (mi *MetricsInterfaceImpl) ObserveMobileClientLoadDuration(platform string, elapsed float64) {
|
func (mi *MetricsInterfaceImpl) ObserveMobileClientLoadDuration(platform string, elapsed float64) {
|
||||||
mi.MobileClientLoadDuration.With(prometheus.Labels{"platform": platform}).Observe(elapsed)
|
mi.MobileClientLoadDuration.With(prometheus.Labels{"platform": platform}).Observe(elapsed)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ const (
|
|||||||
MobileClientChannelSwitchDuration MetricType = "mobile_channel_switch"
|
MobileClientChannelSwitchDuration MetricType = "mobile_channel_switch"
|
||||||
MobileClientTeamSwitchDuration MetricType = "mobile_team_switch"
|
MobileClientTeamSwitchDuration MetricType = "mobile_team_switch"
|
||||||
|
|
||||||
|
DesktopClientCPUUsage MetricType = "desktop_cpu"
|
||||||
|
DesktopClientMemoryUsage MetricType = "desktop_memory"
|
||||||
|
|
||||||
performanceReportTTLMilliseconds = 300 * 1000 // 300 seconds/5 minutes
|
performanceReportTTLMilliseconds = 300 * 1000 // 300 seconds/5 minutes
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -104,8 +107,9 @@ func (r *PerformanceReport) IsValid() error {
|
|||||||
|
|
||||||
func (r *PerformanceReport) ProcessLabels() map[string]string {
|
func (r *PerformanceReport) ProcessLabels() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"platform": processLabel(r.Labels, "platform", acceptedPlatforms, "other"),
|
"platform": processLabel(r.Labels, "platform", acceptedPlatforms, "other"),
|
||||||
"agent": processLabel(r.Labels, "agent", acceptedAgents, "other"),
|
"agent": processLabel(r.Labels, "agent", acceptedAgents, "other"),
|
||||||
|
"desktop_app_version": r.Labels["desktop_app_version"],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"@mattermost/client": "*",
|
"@mattermost/client": "*",
|
||||||
"@mattermost/compass-components": "^0.2.12",
|
"@mattermost/compass-components": "^0.2.12",
|
||||||
"@mattermost/compass-icons": "0.1.39",
|
"@mattermost/compass-icons": "0.1.39",
|
||||||
"@mattermost/desktop-api": "5.8.0-5",
|
"@mattermost/desktop-api": "5.10.0-2",
|
||||||
"@mattermost/types": "*",
|
"@mattermost/types": "*",
|
||||||
"@mui/base": "5.0.0-alpha.127",
|
"@mui/base": "5.0.0-alpha.127",
|
||||||
"@mui/material": "5.11.16",
|
"@mui/material": "5.11.16",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {useStore} from 'react-redux';
|
|||||||
|
|
||||||
import {Client4} from 'mattermost-redux/client';
|
import {Client4} from 'mattermost-redux/client';
|
||||||
|
|
||||||
|
import DesktopAppAPI from 'utils/desktop_api';
|
||||||
import PerformanceReporter from 'utils/performance_telemetry/reporter';
|
import PerformanceReporter from 'utils/performance_telemetry/reporter';
|
||||||
|
|
||||||
export default function PerformanceReporterController() {
|
export default function PerformanceReporterController() {
|
||||||
@@ -14,7 +15,7 @@ export default function PerformanceReporterController() {
|
|||||||
const reporter = useRef<PerformanceReporter>();
|
const reporter = useRef<PerformanceReporter>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reporter.current = new PerformanceReporter(Client4, store);
|
reporter.current = new PerformanceReporter(Client4, store, DesktopAppAPI);
|
||||||
reporter.current.observe();
|
reporter.current.observe();
|
||||||
|
|
||||||
// There's no way to clean up web-vitals, so continue to assume that this component won't ever be unmounted
|
// There's no way to clean up web-vitals, so continue to assume that this component won't ever be unmounted
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DesktopAppAPI {
|
export class DesktopAppAPI {
|
||||||
private name?: string;
|
private name?: string;
|
||||||
private version?: string | null;
|
private version?: string | null;
|
||||||
|
private prereleaseVersion?: string;
|
||||||
private dev?: boolean;
|
private dev?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +33,7 @@ class DesktopAppAPI {
|
|||||||
this.getDesktopAppInfo().then(({name, version}) => {
|
this.getDesktopAppInfo().then(({name, version}) => {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.version = semver.valid(semver.coerce(version));
|
this.version = semver.valid(semver.coerce(version));
|
||||||
|
this.prereleaseVersion = version?.split('-')?.[1];
|
||||||
|
|
||||||
// Legacy Desktop App version, used by some plugins
|
// Legacy Desktop App version, used by some plugins
|
||||||
if (!window.desktop) {
|
if (!window.desktop) {
|
||||||
@@ -63,6 +65,10 @@ class DesktopAppAPI {
|
|||||||
return this.version;
|
return this.version;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getPrereleaseVersion = () => {
|
||||||
|
return this.prereleaseVersion;
|
||||||
|
};
|
||||||
|
|
||||||
isDev = () => {
|
isDev = () => {
|
||||||
return this.dev;
|
return this.dev;
|
||||||
};
|
};
|
||||||
@@ -152,6 +158,10 @@ class DesktopAppAPI {
|
|||||||
return () => this.removePostMessageListener('history-button-return', legacyListener);
|
return () => this.removePostMessageListener('history-button-return', legacyListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onReceiveMetrics = (listener: (metricsMap: Map<string, {cpu?: number; memory?: number}>) => void) => {
|
||||||
|
return window.desktopAPI?.onSendMetrics?.(listener);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One-ways
|
* One-ways
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,3 +37,7 @@ export function getUserAgentLabel() {
|
|||||||
|
|
||||||
return 'other';
|
return 'other';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDesktopAppVersionLabel(appVersion?: string | null, prereleaseVersion?: string) {
|
||||||
|
return prereleaseVersion?.split('.')[0] ?? appVersion ?? 'unknown';
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import configureStore from 'store';
|
|||||||
|
|
||||||
import {reset as resetUserAgent, setPlatform, set as setUserAgent} from 'tests/helpers/user_agent_mocks';
|
import {reset as resetUserAgent, setPlatform, set as setUserAgent} from 'tests/helpers/user_agent_mocks';
|
||||||
import {waitForObservations} from 'tests/performance_mock';
|
import {waitForObservations} from 'tests/performance_mock';
|
||||||
|
import {DesktopAppAPI} from 'utils/desktop_api';
|
||||||
|
|
||||||
import PerformanceReporter from './reporter';
|
import PerformanceReporter from './reporter';
|
||||||
|
|
||||||
@@ -389,7 +390,7 @@ function newTestReporter(telemetryEnabled = true, loggedIn = true) {
|
|||||||
currentUserId: loggedIn ? 'currentUserId' : '',
|
currentUserId: loggedIn ? 'currentUserId' : '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}), new DesktopAppAPI());
|
||||||
|
|
||||||
return {
|
return {
|
||||||
client,
|
client,
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ import type {Client4} from '@mattermost/client';
|
|||||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
|
||||||
|
|
||||||
|
import type {DesktopAppAPI} from 'utils/desktop_api';
|
||||||
|
|
||||||
import type {GlobalState} from 'types/store';
|
import type {GlobalState} from 'types/store';
|
||||||
|
|
||||||
import {identifyElementRegion} from './element_identification';
|
import {identifyElementRegion} from './element_identification';
|
||||||
import type {PerformanceLongTaskTiming} from './long_task';
|
import type {PerformanceLongTaskTiming} from './long_task';
|
||||||
import type {PlatformLabel, UserAgentLabel} from './platform_detection';
|
import type {PlatformLabel, UserAgentLabel} from './platform_detection';
|
||||||
import {getPlatformLabel, getUserAgentLabel} from './platform_detection';
|
import {getDesktopAppVersionLabel, getPlatformLabel, getUserAgentLabel} from './platform_detection';
|
||||||
|
|
||||||
import {Measure} from '.';
|
import {Measure} from '.';
|
||||||
|
|
||||||
@@ -52,6 +54,7 @@ type PerformanceReport = {
|
|||||||
labels: {
|
labels: {
|
||||||
platform: PlatformLabel;
|
platform: PlatformLabel;
|
||||||
agent: UserAgentLabel;
|
agent: UserAgentLabel;
|
||||||
|
desktop_app_version?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
start: number;
|
start: number;
|
||||||
@@ -65,8 +68,12 @@ export default class PerformanceReporter {
|
|||||||
private client: Client4;
|
private client: Client4;
|
||||||
private store: Store<GlobalState>;
|
private store: Store<GlobalState>;
|
||||||
|
|
||||||
|
private desktopAPI: DesktopAppAPI;
|
||||||
|
private desktopOffListener?: () => void;
|
||||||
|
|
||||||
private platformLabel: PlatformLabel;
|
private platformLabel: PlatformLabel;
|
||||||
private userAgentLabel: UserAgentLabel;
|
private userAgentLabel: UserAgentLabel;
|
||||||
|
private desktopAppVersion?: string;
|
||||||
|
|
||||||
private counters: Map<string, number>;
|
private counters: Map<string, number>;
|
||||||
private histogramMeasures: PerformanceReportMeasure[];
|
private histogramMeasures: PerformanceReportMeasure[];
|
||||||
@@ -78,13 +85,17 @@ export default class PerformanceReporter {
|
|||||||
protected reportPeriodBase = 60 * 1000;
|
protected reportPeriodBase = 60 * 1000;
|
||||||
protected reportPeriodJitter = 15 * 1000;
|
protected reportPeriodJitter = 15 * 1000;
|
||||||
|
|
||||||
constructor(client: Client4, store: Store<GlobalState>) {
|
constructor(client: Client4, store: Store<GlobalState>, desktopAPI: DesktopAppAPI) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
|
this.desktopAPI = desktopAPI;
|
||||||
|
|
||||||
this.platformLabel = getPlatformLabel();
|
this.platformLabel = getPlatformLabel();
|
||||||
this.userAgentLabel = getUserAgentLabel();
|
this.userAgentLabel = getUserAgentLabel();
|
||||||
|
|
||||||
|
// We want to submit by prerelease version if it exists, so we don't muddy up the metrics for the release builds
|
||||||
|
this.desktopAppVersion = getDesktopAppVersionLabel(desktopAPI.getAppVersion(), desktopAPI.getPrereleaseVersion());
|
||||||
|
|
||||||
this.counters = new Map();
|
this.counters = new Map();
|
||||||
this.histogramMeasures = [];
|
this.histogramMeasures = [];
|
||||||
|
|
||||||
@@ -121,6 +132,10 @@ export default class PerformanceReporter {
|
|||||||
// Send any remaining metrics when the page becomes hidden rather than when it's unloaded because that's
|
// Send any remaining metrics when the page becomes hidden rather than when it's unloaded because that's
|
||||||
// what's recommended by various sites due to unload handlers being unreliable, particularly on mobile.
|
// what's recommended by various sites due to unload handlers being unreliable, particularly on mobile.
|
||||||
addEventListener('visibilitychange', this.handleVisibilityChange);
|
addEventListener('visibilitychange', this.handleVisibilityChange);
|
||||||
|
|
||||||
|
if (!this.desktopAPI.isDev()) {
|
||||||
|
this.desktopOffListener = this.desktopAPI.onReceiveMetrics((metrics) => this.collectDesktopAppMetrics(metrics));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private measurePageLoad() {
|
private measurePageLoad() {
|
||||||
@@ -147,6 +162,8 @@ export default class PerformanceReporter {
|
|||||||
this.reportTimeout = undefined;
|
this.reportTimeout = undefined;
|
||||||
|
|
||||||
this.observer.disconnect();
|
this.observer.disconnect();
|
||||||
|
|
||||||
|
this.desktopOffListener?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleObservations(list: PerformanceObserverEntryList) {
|
protected handleObservations(list: PerformanceObserverEntryList) {
|
||||||
@@ -279,6 +296,7 @@ export default class PerformanceReporter {
|
|||||||
labels: {
|
labels: {
|
||||||
platform: this.platformLabel,
|
platform: this.platformLabel,
|
||||||
agent: this.userAgentLabel,
|
agent: this.userAgentLabel,
|
||||||
|
desktop_app_version: this.desktopAppVersion,
|
||||||
},
|
},
|
||||||
|
|
||||||
...this.getReportStartEnd(now, histogramMeasures, counterMeasures),
|
...this.getReportStartEnd(now, histogramMeasures, counterMeasures),
|
||||||
@@ -341,6 +359,35 @@ export default class PerformanceReporter {
|
|||||||
|
|
||||||
return navigator.sendBeacon(url, data);
|
return navigator.sendBeacon(url, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected collectDesktopAppMetrics(metricsMap: Map<string, {cpu?: number; memory?: number}>) {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
for (const [processName, metrics] of metricsMap.entries()) {
|
||||||
|
let process = processName;
|
||||||
|
if (process.startsWith('Server ')) {
|
||||||
|
process = 'Server';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metrics.cpu) {
|
||||||
|
this.histogramMeasures.push({
|
||||||
|
metric: 'desktop_cpu',
|
||||||
|
timestamp: now,
|
||||||
|
labels: {process},
|
||||||
|
value: metrics.cpu,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metrics.memory) {
|
||||||
|
this.histogramMeasures.push({
|
||||||
|
metric: 'desktop_memory',
|
||||||
|
timestamp: now,
|
||||||
|
labels: {process},
|
||||||
|
value: metrics.memory,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPerformanceLongTask(entry: PerformanceEntry): entry is PerformanceLongTaskTiming {
|
function isPerformanceLongTask(entry: PerformanceEntry): entry is PerformanceLongTaskTiming {
|
||||||
|
|||||||
28
webapp/package-lock.json
сгенерированный
28
webapp/package-lock.json
сгенерированный
@@ -62,7 +62,7 @@
|
|||||||
"@mattermost/client": "*",
|
"@mattermost/client": "*",
|
||||||
"@mattermost/compass-components": "^0.2.12",
|
"@mattermost/compass-components": "^0.2.12",
|
||||||
"@mattermost/compass-icons": "0.1.39",
|
"@mattermost/compass-icons": "0.1.39",
|
||||||
"@mattermost/desktop-api": "5.8.0-5",
|
"@mattermost/desktop-api": "5.10.0-2",
|
||||||
"@mattermost/types": "*",
|
"@mattermost/types": "*",
|
||||||
"@mui/base": "5.0.0-alpha.127",
|
"@mui/base": "5.0.0-alpha.127",
|
||||||
"@mui/material": "5.11.16",
|
"@mui/material": "5.11.16",
|
||||||
@@ -227,6 +227,19 @@
|
|||||||
"yargs": "16.2.0"
|
"yargs": "16.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"channels/node_modules/@mattermost/desktop-api": {
|
||||||
|
"version": "5.10.0-2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mattermost/desktop-api/-/desktop-api-5.10.0-2.tgz",
|
||||||
|
"integrity": "sha512-Okb+VP6gdwEBnSthzxiU3+oO5XLTocDvWlzoGYjBYrT4DN2/xxAzRYgp1VB6skQxEwVhbP/zaQvWbRtZrp8org==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^4.3.0 || ^5.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"channels/node_modules/semver": {
|
"channels/node_modules/semver": {
|
||||||
"version": "7.3.8",
|
"version": "7.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||||
@@ -4310,19 +4323,6 @@
|
|||||||
"resolved": "platform/components",
|
"resolved": "platform/components",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@mattermost/desktop-api": {
|
|
||||||
"version": "5.8.0-5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mattermost/desktop-api/-/desktop-api-5.8.0-5.tgz",
|
|
||||||
"integrity": "sha512-YPtFRnduVFOXyK25GedJA+PkAKmFpLDKqfxvV/IIS+SMypv6BD17LJ8AkdBsguFFa6ZWLlZU40M5grKYBbjOuA==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^4.3.0 || ^5.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"typescript": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mattermost/eslint-plugin": {
|
"node_modules/@mattermost/eslint-plugin": {
|
||||||
"resolved": "platform/eslint-plugin",
|
"resolved": "platform/eslint-plugin",
|
||||||
"link": true
|
"link": true
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user