diff --git a/api_docs/core.json b/api_docs/core.json index ba8f27d50d13c..7129c2cff14de 100644 --- a/api_docs/core.json +++ b/api_docs/core.json @@ -1431,7 +1431,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 282 + "lineNumber": 283 } }, { @@ -1442,7 +1442,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 283 + "lineNumber": 284 } }, { @@ -1453,7 +1453,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 284 + "lineNumber": 285 }, "signature": [ "{ readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; }; readonly auditbeat: { readonly base: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly enterpriseSearch: { readonly base: string; readonly appSearchBase: string; readonly workplaceSearchBase: string; }; readonly heartbeat: { readonly base: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite_missing_bucket: string; readonly date_histogram: string; readonly date_range: string; readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; readonly histogram: string; readonly ip_range: string; readonly range: string; readonly significant_terms: string; readonly terms: string; readonly avg: string; readonly avg_bucket: string; readonly max_bucket: string; readonly min_bucket: string; readonly sum_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative_sum: string; readonly derivative: string; readonly geo_bounds: string; readonly geo_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving_avg: string; readonly percentile_ranks: string; readonly serial_diff: string; readonly std_dev: string; readonly sum: string; readonly top_hits: string; }; readonly runtimeFields: string; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly indexPatterns: { readonly loadingData: string; readonly introduction: string; }; readonly addData: string; readonly kibana: string; readonly elasticsearch: Record; readonly siem: { readonly guide: string; readonly gettingStarted: string; }; readonly query: { readonly eql: string; readonly luceneQuerySyntax: string; readonly queryDsl: string; readonly kueryQuerySyntax: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record; readonly ml: Record; readonly transforms: Record; readonly visualize: Record; readonly apis: Readonly<{ createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putWatch: string; updateTransform: string; }>; readonly observability: Record; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; readonly snapshotRestore: Record; }" @@ -1462,7 +1462,7 @@ ], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 281 + "lineNumber": 282 }, "initialIsOpen": false }, diff --git a/package.json b/package.json index 2d96766952347..32cf8dc1aee0f 100644 --- a/package.json +++ b/package.json @@ -347,7 +347,7 @@ "@cypress/webpack-preprocessor": "^5.5.0", "@elastic/apm-rum": "^5.6.1", "@elastic/apm-rum-react": "^1.2.5", - "@elastic/charts": "25.1.0", + "@elastic/charts": "25.3.0", "@elastic/eslint-config-kibana": "link:packages/elastic-eslint-config-kibana", "@elastic/eslint-plugin-eui": "0.0.2", "@elastic/github-checks-reporter": "0.0.20b3", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 206c5c62d2472..9bf332bf82319 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -59566,18 +59566,26 @@ async function runCommand(command, config) { if (command.reportTiming) { // if we don't have a kbn object then things are too broken to report on if (kbn) { - const reporter = _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__["CiStatsReporter"].fromEnv(_utils_log__WEBPACK_IMPORTED_MODULE_2__["log"]); - await reporter.timings({ - upstreamBranch: kbn.kibanaProject.json.branch, - timings: [{ - group: command.reportTiming.group, - id: command.reportTiming.id, - ms: Date.now() - runStartTime, - meta: { - success: false - } - }] - }); + try { + const reporter = _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__["CiStatsReporter"].fromEnv(_utils_log__WEBPACK_IMPORTED_MODULE_2__["log"]); + await reporter.timings({ + upstreamBranch: kbn.kibanaProject.json.branch, + // prevent loading @kbn/utils by passing null + kibanaUuid: kbn.getUuid() || null, + timings: [{ + group: command.reportTiming.group, + id: command.reportTiming.id, + ms: Date.now() - runStartTime, + meta: { + success: false + } + }] + }); + } catch (e) { + // prevent hiding bootstrap errors + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error('failed to report timings:'); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(e); + } } } diff --git a/packages/kbn-pm/src/run.ts b/packages/kbn-pm/src/run.ts index e5d74cee65ba7..ae3669ff3b16b 100644 --- a/packages/kbn-pm/src/run.ts +++ b/packages/kbn-pm/src/run.ts @@ -69,20 +69,28 @@ export async function runCommand(command: ICommand, config: Omit>; +interface Props { + setCurrentStep: React.Dispatch>; + checksInProgress: boolean; + validationMessages: CalloutMessage[]; } -export const ValidationStep: FC = ({ state, setCurrentStep, setValidationSummary }) => { - const [checksInProgress, setChecksInProgress] = useState(false); - const [validationMessages, setValidationMessages] = useState([]); - const [errorMessage, setErrorMessage] = useState(); - - const { form, jobConfig, isAdvancedEditorEnabled } = state; - const { - dataFrameAnalytics: { validateDataFrameAnalytics }, - } = useMlApiContext(); - - const runValidationChecks = async () => { - try { - const analyticsJobConfig = (isAdvancedEditorEnabled - ? jobConfig - : getJobConfigFromFormState(form)) as DataFrameAnalyticsConfig; - const validationResults: ValidateAnalyticsJobResponse = await validateDataFrameAnalytics( - analyticsJobConfig - ); - - const validationSummary = { warning: 0, success: 0 }; - validationResults.forEach((message) => { - if (message?.status === VALIDATION_STATUS.WARNING) { - validationSummary.warning++; - } else if (message?.status === VALIDATION_STATUS.SUCCESS) { - validationSummary.success++; - } - }); - - setValidationMessages(validationResults); - setValidationSummary(validationSummary); - setChecksInProgress(false); - } catch (err) { - setErrorMessage({ - heading: i18n.translate( - 'xpack.ml.dataframe.analytics.validation.validationFetchErrorMessage', - { - defaultMessage: 'Error validating job', - } - ), - id: 'error', - status: VALIDATION_STATUS.ERROR, - text: extractErrorMessage(err), - }); - setChecksInProgress(false); - } - }; - - useEffect(function beginValidationChecks() { - setChecksInProgress(true); - runValidationChecks(); - }, []); - - if (errorMessage !== undefined) { - validationMessages.push(errorMessage); - } - +export const ValidationStep: FC = ({ + checksInProgress, + validationMessages, + setCurrentStep, +}) => { const callouts = validationMessages.map((m, i) => ); return ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx index 9d918787db91b..25030801d8580 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx @@ -5,13 +5,23 @@ * 2.0. */ -import React, { FC, useState } from 'react'; +import React, { FC, useEffect, useMemo, useState } from 'react'; import { EuiForm } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { debounce } from 'lodash'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { ValidationStep } from './validation_step'; import { ValidationStepDetails } from './validation_step_details'; import { ANALYTICS_STEPS } from '../../page'; +import { useMlApiContext } from '../../../../../contexts/kibana'; +import { getJobConfigFromFormState } from '../../../analytics_management/hooks/use_create_analytics_form/state'; +import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { + CalloutMessage, + ValidateAnalyticsJobResponse, + VALIDATION_STATUS, +} from '../../../../../../../common/constants/validation'; export interface ValidationSummary { warning: number; @@ -19,32 +29,105 @@ export interface ValidationSummary { } export const ValidationStepWrapper: FC = ({ - actions, state, setCurrentStep, step, stepActivated, }) => { - const [validationSummary, setValidationSummary] = useState({ - warning: 0, - success: 0, - }); - + const [checksInProgress, setChecksInProgress] = useState(false); + const [validationMessages, setValidationMessages] = useState([]); + const [errorMessage, setErrorMessage] = useState(); + const { form, jobConfig, isAdvancedEditorEnabled } = state; + const { + dependentVariable, + jobType, + trainingPercent, + numTopFeatureImportanceValues, + numTopClasses, + includes, + } = form; const showValidationStep = step === ANALYTICS_STEPS.VALIDATION; const showDetails = step !== ANALYTICS_STEPS.VALIDATION && stepActivated === true; + const { + dataFrameAnalytics: { validateDataFrameAnalytics }, + } = useMlApiContext(); const dataTestSubj = `mlAnalyticsCreateJobWizardValidationStepWrapper${ showValidationStep ? ' active' : '' }${showDetails ? ' summary' : ''}`; + const debouncedValidationChecks = debounce(async () => { + setChecksInProgress(true); + try { + const analyticsJobConfig = isAdvancedEditorEnabled + ? jobConfig + : getJobConfigFromFormState(form); + const validationResults: ValidateAnalyticsJobResponse = await validateDataFrameAnalytics( + analyticsJobConfig + ); + + setValidationMessages(validationResults); + setErrorMessage(undefined); + setChecksInProgress(false); + } catch (err) { + setErrorMessage({ + heading: i18n.translate( + 'xpack.ml.dataframe.analytics.validation.validationFetchErrorMessage', + { + defaultMessage: 'Error validating job', + } + ), + id: 'error', + status: VALIDATION_STATUS.ERROR, + text: extractErrorMessage(err), + }); + setChecksInProgress(false); + } + }, 500); + + useEffect( + function beginValidationChecks() { + if (jobType !== undefined && (showValidationStep || stepActivated === true)) { + debouncedValidationChecks(); + } + }, + [ + showValidationStep, + dependentVariable, + trainingPercent, + numTopFeatureImportanceValues, + numTopClasses, + includes, + ] + ); + + if (errorMessage !== undefined) { + validationMessages.push(errorMessage); + } + + const validationSummary = useMemo( + () => + validationMessages.reduce( + (acc, message) => { + if (message?.status === VALIDATION_STATUS.WARNING) { + acc.warning += 1; + } else if (message?.status === VALIDATION_STATUS.SUCCESS) { + acc.success += 1; + } + return acc; + }, + { warning: 0, success: 0 } + ), + [validationMessages] + ); + return ( {showValidationStep && ( )} {showDetails && ( diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts index ce07eb9466c34..371446638814a 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts @@ -9,17 +9,26 @@ import { i18n } from '@kbn/i18n'; import { IScopedClusterClient } from 'kibana/server'; import { getAnalysisType } from '../../../common/util/analytics_utils'; import { + ALL_CATEGORIES, + FRACTION_EMPTY_LIMIT, INCLUDED_FIELDS_THRESHOLD, MINIMUM_NUM_FIELD_FOR_CHECK, - FRACTION_EMPTY_LIMIT, + NUM_CATEGORIES_THRESHOLD, TRAINING_DOCS_LOWER, TRAINING_DOCS_UPPER, VALIDATION_STATUS, } from '../../../common/constants/validation'; -import { getDependentVar } from '../../../common/util/analytics_utils'; +import { + getDependentVar, + isRegressionAnalysis, + isClassificationAnalysis, +} from '../../../common/util/analytics_utils'; import { extractErrorMessage } from '../../../common/util/errors'; import { SearchResponse7 } from '../../../common'; -import { DataFrameAnalyticsConfig } from '../../../common/types/data_frame_analytics'; +import { + AnalysisConfig, + DataFrameAnalyticsConfig, +} from '../../../common/types/data_frame_analytics'; interface MissingAgg { [key: string]: { @@ -69,6 +78,87 @@ const analysisFieldsWarningMessage = { heading: analysisFieldsHeading, }; +function getRegressionAndClassificationMessage( + analysisConfig: AnalysisConfig, + analysisType: string, + totalDocs: number, + depVarCardinality: number | undefined +) { + const messages = []; + if (isRegressionAnalysis(analysisConfig) || isClassificationAnalysis(analysisConfig)) { + const trainingPercent = analysisConfig[analysisType].training_percent; + const featureImportance = analysisConfig[analysisType].num_top_feature_importance_values; + const topClasses: number | undefined = isClassificationAnalysis(analysisConfig) + ? analysisConfig[analysisType].num_top_classes + : undefined; + + if (trainingPercent) { + const trainingDocs = totalDocs * (trainingPercent / 100); + const trainingPercentMessage = getTrainingPercentMessage(trainingDocs); + if (trainingPercentMessage) { + messages.push(trainingPercentMessage); + } + } + + if (featureImportance && featureImportance > 0) { + messages.push({ + id: 'feature_importance', + text: i18n.translate( + 'xpack.ml.models.dfaValidation.messages.featureImportanceWarningMessage', + { + defaultMessage: + 'Enabling feature importance can result in long running jobs when there are a large number of training documents.', + } + ), + status: VALIDATION_STATUS.WARNING, + heading: i18n.translate('xpack.ml.models.dfaValidation.messages.featureImportanceHeading', { + defaultMessage: 'Feature importance', + }), + }); + } + + if (topClasses !== undefined) { + if ( + (topClasses === ALL_CATEGORIES && + depVarCardinality && + depVarCardinality > NUM_CATEGORIES_THRESHOLD) || + topClasses > NUM_CATEGORIES_THRESHOLD + ) { + messages.push({ + id: 'num_top_classes', + text: i18n.translate('xpack.ml.models.dfaValidation.messages.topClassesWarningMessage', { + defaultMessage: + 'Probabilities will be reported for {numCategories, plural, one {# category} other {# categories}}. There could be a significant effect on the size of your destination index.', + values: { + numCategories: topClasses === ALL_CATEGORIES ? depVarCardinality : topClasses, + }, + }), + status: VALIDATION_STATUS.WARNING, + heading: i18n.translate('xpack.ml.models.dfaValidation.messages.topClassesHeading', { + defaultMessage: 'Top classes', + }), + }); + } else { + messages.push({ + id: 'num_top_classes', + text: i18n.translate('xpack.ml.models.dfaValidation.messages.topClassesSuccessMessage', { + defaultMessage: + 'Probabilities will be reported for {numCategories, plural, one {# category} other {# categories}}.', + values: { + numCategories: topClasses === ALL_CATEGORIES ? depVarCardinality : topClasses, + }, + }), + status: VALIDATION_STATUS.SUCCESS, + heading: i18n.translate('xpack.ml.models.dfaValidation.messages.topClassesHeading', { + defaultMessage: 'Top classes', + }), + }); + } + } + } + return messages; +} + function getTrainingPercentMessage(trainingDocs: number) { if (trainingDocs >= TRAINING_DOCS_UPPER) { return { @@ -106,14 +196,17 @@ async function getValidationCheckMessages( asCurrentUser: IScopedClusterClient['asCurrentUser'], analyzedFields: string[], index: string | string[], - query: any = defaultQuery, - depVar: string, - trainingPercent?: number + analysisConfig: AnalysisConfig, + query: unknown = defaultQuery ) { + const analysisType = getAnalysisType(analysisConfig); + const depVar = getDependentVar(analysisConfig); const messages = []; const emptyFields: string[] = []; const percentEmptyLimit = FRACTION_EMPTY_LIMIT * 100; + let depVarValid = true; + let depVarCardinality: number | undefined; let analysisFieldsNumHigh = false; let analysisFieldsEmpty = false; @@ -128,14 +221,11 @@ async function getValidationCheckMessages( }, {} as any); if (depVar !== '') { - const depVarAgg = - depVar !== '' - ? { - [`${depVar}_const`]: { - cardinality: { field: depVar }, - }, - } - : {}; + const depVarAgg = { + [`${depVar}_const`]: { + cardinality: { field: depVar }, + }, + }; aggs = { ...aggs, ...depVarAgg }; } @@ -153,18 +243,6 @@ async function getValidationCheckMessages( const totalDocs = body.hits.total.value; - if (trainingPercent) { - const trainingDocs = totalDocs * (trainingPercent / 100); - const trainingPercentMessage = getTrainingPercentMessage(trainingDocs); - if (trainingPercentMessage) { - messages.push(trainingPercentMessage); - } - - if (analyzedFields.length && analyzedFields.length > INCLUDED_FIELDS_THRESHOLD) { - analysisFieldsNumHigh = true; - } - } - if (body.aggregations) { Object.entries(body.aggregations).forEach(([aggName, { doc_count: docCount, value }]) => { const empty = docCount / totalDocs; @@ -186,6 +264,7 @@ async function getValidationCheckMessages( } if (aggName === `${depVar}_const`) { + depVarCardinality = value; if (value === 1) { depVarValid = false; dependentVarWarningMessage.text = i18n.translate( @@ -212,6 +291,18 @@ async function getValidationCheckMessages( }); } + const regressionAndClassificationMessages = getRegressionAndClassificationMessage( + analysisConfig, + analysisType, + totalDocs, + depVarCardinality + ); + messages.push(...regressionAndClassificationMessages); + + if (analyzedFields.length && analyzedFields.length > INCLUDED_FIELDS_THRESHOLD) { + analysisFieldsNumHigh = true; + } + if (emptyFields.length) { analysisFieldsEmpty = true; } @@ -278,18 +369,12 @@ export async function validateAnalyticsJob( client: IScopedClusterClient, job: DataFrameAnalyticsConfig ) { - const analysisType = getAnalysisType(job.analysis); - const analysis = job.analysis[analysisType]; - const depVar = getDependentVar(job.analysis); - const messages = await getValidationCheckMessages( client.asCurrentUser, job.analyzed_fields.includes, job.source.index, - job.source.query, - depVar, - // @ts-ignore - analysis.training_percent + job.analysis, + job.source.query ); return messages; } diff --git a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts index c1acb801d5c0d..503b67dc31fbc 100644 --- a/x-pack/plugins/remote_clusters/public/application/services/documentation.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts @@ -13,13 +13,10 @@ export let transportPortUrl: string; export let proxyModeUrl: string; export let proxySettingsUrl: string; -export function init(docLinks: DocLinksStart): void { - const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; - const esDocBasePath = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; - - skippingDisconnectedClustersUrl = `${esDocBasePath}/modules-cross-cluster-search.html#_skipping_disconnected_clusters`; - remoteClustersUrl = `${esDocBasePath}/modules-remote-clusters.html`; - transportPortUrl = `${esDocBasePath}/modules-transport.html`; - proxyModeUrl = `${esDocBasePath}/modules-remote-clusters.html#proxy-mode`; - proxySettingsUrl = `${esDocBasePath}/modules-remote-clusters.html#remote-cluster-proxy-settings`; +export function init({ links }: DocLinksStart): void { + skippingDisconnectedClustersUrl = `${links.ccs.skippingDisconnectedClusters}`; + remoteClustersUrl = `${links.elasticsearch.remoteClusters}`; + transportPortUrl = `${links.elasticsearch.transportSettings}`; + proxyModeUrl = `${links.elasticsearch.remoteClustersProxy}`; + proxySettingsUrl = `${links.elasticsearch.remoteClusersProxySettings}`; } diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts index f0b80e86f2a82..cbd840f7a2c56 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/cloning.ts @@ -204,9 +204,13 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep('Should have validation callouts'); await ml.dataFrameAnalyticsCreation.assertValidationCalloutsExists(); - await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent( - testData?.job?.analysis?.outlier_detection !== undefined ? 1 : 3 - ); + if (testData?.job?.analysis?.outlier_detection !== undefined) { + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent(1); + } else { + await ml.dataFrameAnalyticsCreation.assertAllValidationCalloutsPresent( + testData?.job?.analysis?.regression !== undefined ? 3 : 4 + ); + } await ml.testExecution.logTestStep('should continue to the create step'); await ml.dataFrameAnalyticsCreation.continueToCreateStep(); diff --git a/yarn.lock b/yarn.lock index d50e94210707e..74bca3901dfe1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1349,10 +1349,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-25.1.0.tgz#8859e07c9822696b3f9b12ae46a18e3316f623fd" - integrity sha512-4IFmyhg3dG7VjP0C9DbMBBAxAtai/HPdG/4Z5UMdGYGoZiJwS+fo50irQtvD3H5Lm6OSS8Xk9hmCVPx8KmdiUg== +"@elastic/charts@25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-25.3.0.tgz#03cd872907391be415f75209a73298eeffae3ce4" + integrity sha512-40LCVJ/X4uxk/4j+3PIiW596gfevcAUD9NkcMOEbhEgdS0OLTddmg5AHkRM6mfOV96GTrGjX6MB+qAoDgVOeBw== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0"